Самый пифонический способ чередования двух строк

Какой самый пифонический способ сшить две строки вместе?

Например:

Входные данные:

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijklmnopqrstuvwxyz' 

Вывод:

 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

14 Solutions collect form web for “Самый пифонический способ чередования двух строк”

Для меня самый pythonic * путь следующий, который в значительной степени делает то же самое, но использует оператор + для конкатенации отдельных символов в каждой строке:

 res = "".join(i + j for i, j in zip(u, l)) print(res) # 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

Это также быстрее, чем использование двух вызовов join() :

 In [5]: l1 = 'A' * 1000000; l2 = 'a' * 1000000 In [6]: %timeit "".join("".join(item) for item in zip(l1, l2)) 1 loops, best of 3: 442 ms per loop In [7]: %timeit "".join(i + j for i, j in zip(l1, l2)) 1 loops, best of 3: 360 ms per loop 

Существуют более быстрые подходы, но они часто обфускают код.

Примечание. Если две входные строки имеют одинаковую длину, то более длинный будет усечен, так как zip перестанет итерации в конце более короткой строки. В этом случае вместо zip следует использовать zip_longest ( izip_longest в Python 2) из ​​модуля itertools чтобы гарантировать, что обе строки полностью исчерпаны.


* Чтобы сделать цитату из Zen of Python : количество читаемости .
Pythonic = читаемость для меня; i + j просто визуально анализируется легче, по крайней мере для моих глаз.

Более быстрая альтернатива

Другой путь:

 res = [''] * len(u) * 2 res[::2] = u res[1::2] = l print(''.join(res)) 

Вывод:

 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

скорость

Похоже, что это быстрее:

 %%timeit res = [''] * len(u) * 2 res[::2] = u res[1::2] = l ''.join(res) 100000 loops, best of 3: 4.75 µs per loop 

чем самое быстрое решение:

 %timeit "".join(list(chain.from_iterable(zip(u, l)))) 100000 loops, best of 3: 6.52 µs per loop 

Также для больших строк:

 l1 = 'A' * 1000000; l2 = 'a' * 1000000 %timeit "".join(list(chain.from_iterable(zip(l1, l2)))) 1 loops, best of 3: 151 ms per loop %%timeit res = [''] * len(l1) * 2 res[::2] = l1 res[1::2] = l2 ''.join(res) 10 loops, best of 3: 92 ms per loop 

Python 3.5.1.

Изменение для строк с разной длиной

 u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijkl' 

Более короткий определяет длину ( zip() эквивалент)

 min_len = min(len(u), len(l)) res = [''] * min_len * 2 res[::2] = u[:min_len] res[1::2] = l[:min_len] print(''.join(res)) 

Вывод:

 AaBbCcDdEeFfGgHhIiJjKkLl 

Более длинный определяет длину ( itertools.zip_longest(fillvalue='') эквивалент)

 min_len = min(len(u), len(l)) res = [''] * min_len * 2 res[::2] = u[:min_len] res[1::2] = l[:min_len] res += u[min_len:] + l[min_len:] print(''.join(res)) 

Вывод:

 AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ 

С join() и zip() .

 >>> ''.join(''.join(item) for item in zip(u,l)) 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

На Python 2, безусловно, более быстрый способ сделать что-то, при ~ 3x скорость нарезки списка для небольших строк и ~ 30x для длинных

 res = bytearray(len(u) * 2) res[::2] = u res[1::2] = l str(res) 

Однако это не повлияло бы на Python 3. Вы могли бы реализовать что-то вроде

 res = bytearray(len(u) * 2) res[::2] = u.encode("ascii") res[1::2] = l.encode("ascii") res.decode("ascii") 

но к тому времени вы уже потеряли выигрыш над сортировкой списков для небольших строк (это все равно 20x скорость для длинных строк), и это даже не работает для символов, отличных от ASCII.

FWIW, если вы делаете это на массивных строках и нуждаетесь в каждом цикле и по какой-то причине должны использовать строки Python … вот как это сделать:

 res = bytearray(len(u) * 4 * 2) u_utf32 = u.encode("utf_32_be") res[0::8] = u_utf32[0::4] res[1::8] = u_utf32[1::4] res[2::8] = u_utf32[2::4] res[3::8] = u_utf32[3::4] l_utf32 = l.encode("utf_32_be") res[4::8] = l_utf32[0::4] res[5::8] = l_utf32[1::4] res[6::8] = l_utf32[2::4] res[7::8] = l_utf32[3::4] res.decode("utf_32_be") 

Особый обложку также поможет общий случай меньших типов. FWIW, это всего лишь 3 раза скорость сортировки списков для длинных строк и коэффициент от 4 до 5 медленнее для небольших строк.

В любом случае, я предпочитаю join решения, но поскольку тайминги были упомянуты в другом месте, я думал, что могу присоединиться.

Если вам нужен самый быстрый способ, вы можете комбинировать itertools с operator.add :

 In [36]: from operator import add In [37]: from itertools import starmap, izip In [38]: timeit "".join([i + j for i, j in uzip(l1, l2)]) 1 loops, best of 3: 142 ms per loop In [39]: timeit "".join(starmap(add, izip(l1,l2))) 1 loops, best of 3: 117 ms per loop In [40]: timeit "".join(["".join(item) for item in zip(l1, l2)]) 1 loops, best of 3: 196 ms per loop In [41]: "".join(starmap(add, izip(l1,l2))) == "".join([i + j for i, j in izip(l1, l2)]) == "".join(["".join(item) for item in izip(l1, l2)]) Out[42]: True 

Но объединение izip и chain.from_iterable происходит быстрее

 In [2]: from itertools import chain, izip In [3]: timeit "".join(chain.from_iterable(izip(l1, l2))) 10 loops, best of 3: 98.7 ms per loop 

Существует также существенная разница между chain(* и chain.from_iterable(... .

 In [5]: timeit "".join(chain(*izip(l1, l2))) 1 loops, best of 3: 212 ms per loop 

Нет такой вещи, как генератор с соединением, проходящий один из них всегда будет медленнее, поскольку python сначала построит список, используя контент, потому что он выполняет два прохода над данными, один для определения необходимого размера и один для фактического выполнения соединение, которое было бы невозможно с помощью генератора:

join.h :

  /* Here is the general case. Do a pre-pass to figure out the total * amount of space we'll need (sz), and see whether all arguments are * bytes-like. */ 

Также, если у вас разные строки длины, и вы не хотите потерять данные, вы можете использовать izip_longest :

 In [22]: from itertools import izip_longest In [23]: a,b = "hlo","elworld" In [24]: "".join(chain.from_iterable(izip_longest(a, b,fillvalue=""))) Out[24]: 'helloworld' 

Для python 3 он называется zip_longest

Но для python2 предложение veedrac является самым быстрым:

 In [18]: %%timeit res = bytearray(len(u) * 2) res[::2] = u res[1::2] = l str(res) ....: 100 loops, best of 3: 2.68 ms per loop 

Вы также можете сделать это, используя map и operator.add :

 from operator import add u = 'AAAAA' l = 'aaaaa' s = "".join(map(add, u, l)) 

Выход :

 'AaAaAaAaAa' 

Какая карта состоит в том, что он берет каждый элемент из первого итеративного u и первых элементов из второго итерабельного l и применяет функцию, предоставленную в качестве первого аргумента add . Затем соединение просто присоединяется к ним.

Многие из этих предложений предполагают, что строки имеют одинаковую длину. Возможно, это охватывает все разумные варианты использования, но по крайней мере для меня кажется, что вы, возможно, захотите также разместить строки различной длины. Или я единственный, кто думает, что сетка должна работать немного так:

 u = "foobar" l = "baz" mesh(u,l) = "fboaozbar" 

Один из способов сделать это будет следующим:

 def mesh(a,b): minlen = min(len(a),len(b)) return "".join(["".join(x+y for x,y in zip(a,b)),a[minlen:],b[minlen:]]) 

Ответ Джима велик, но вот мой любимый вариант, если вы не возражаете против нескольких импортов:

 from functools import reduce from operator import add reduce(add, map(add, u, l)) 

Мне нравится использовать два for s, имена переменных могут давать подсказку / напоминание о том, что происходит:

 "".join(char for pair in zip(u,l) for char in pair) 

Просто добавьте еще один, более базовый подход:

 st = "" for char in u: st = "{0}{1}{2}".format( st, char, l[ u.index( char ) ] ) 

Потенциально быстрее и короче, чем текущее ведущее решение:

 from itertools import chain u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijklmnopqrstuvwxyz' res = "".join(chain(*zip(u, l))) 

Стратегия по скорости – это сделать как можно больше на C-уровне. То же самое zip_longest () исправить для неровных строк, и он выйдет из того же модуля, что и цепочка (), поэтому не может набить мне слишком много очков!

Другие решения, которые я придумал на этом пути:

 res = "".join(u[x] + l[x] for x in range(len(u))) res = "".join(k + l[i] for i, k in enumerate(u)) 

Чувствует себя немного не-pythonic, чтобы не учитывать ответ на двойной список, здесь обрабатывать n строк с помощью O (1):

 "".join(c for cs in itertools.zip_longest(*all_strings) for c in cs) 

где all_strings – список строк, которые вы хотите чередовать. В вашем случае all_strings = [u, l] . Пример полного использования будет выглядеть так:

 import itertools a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' b = 'abcdefghijklmnopqrstuvwxyz' all_strings = [a,b] interleaved = "".join(c for cs in itertools.zip_longest(*all_strings) for c in cs) print(interleaved) # 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' с import itertools a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' b = 'abcdefghijklmnopqrstuvwxyz' all_strings = [a,b] interleaved = "".join(c for cs in itertools.zip_longest(*all_strings) for c in cs) print(interleaved) # 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

Как и многие ответы, быстрее? Наверное, нет, но просто и гибко. Кроме того, без слишком сложной сложности это немного быстрее, чем принятый ответ (в общем случае добавление строк в python немного медленнее):

 In [7]: l1 = 'A' * 1000000; l2 = 'a' * 1000000; In [8]: %timeit "".join(a + b for i, j in zip(l1, l2)) 1 loops, best of 3: 227 ms per loop In [9]: %timeit "".join(c for cs in zip(*(l1, l2)) for c in cs) 1 loops, best of 3: 198 ms per loop 

Вы можете использовать iteration_utilities.roundrobin 1

 u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijklmnopqrstuvwxyz' from iteration_utilities import roundrobin ''.join(roundrobin(u, l)) # returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

или класс ManyIterables из того же пакета:

 from iteration_utilities import ManyIterables ManyIterables(u, l).roundrobin().as_string() # returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

1 Это из сторонней библиотеки, которую я написал: iteration_utilities .

Я бы использовал zip (), чтобы получить читаемый и простой способ:

 result = '' for cha, chb in zip(u, l): result += '%s%s' % (cha, chb) print result # 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 
  • Оптимизация строчной строки Python
  • Преобразование строки в кортеж
  • Как расширить строку внутри строки в python?
  • Фильтрация pandas dataframe rows by содержит str
  • Как декодировать строку python
  • Поиск строк с использованием регулярного выражения в Python
  • Скорость копирования буфера Python - почему массив медленнее, чем строка?
  • Преобразовать символы ASCII в латинские буквы Unicode FULLWIDTH в Python?
  • Python - лучший язык программирования в мире.