Який найпітонічніший спосіб з’єднати дві струни разом?
Наприклад:
Вхід:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Вихід:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Який найпітонічніший спосіб з’єднати дві струни разом?
Наприклад:
Вхід:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Вихід:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Відповіді:
Для мене найбільш пітонічним * способом є наступний, який в значній мірі робить те саме, але використовує +оператор для об'єднання окремих символів у кожному рядку:
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для забезпечення повного вичерпання обох рядків.
* Щоб взяти цитату з дзен Python : Читання рахується .
Pythonic = читабельність для мене; i + jпросто візуально розбирається легше, принаймні для моїх очей.
"".join([i + j for i, j in zip(l1, l2)])і це, безумовно, буде найшвидшим
"".join(map("".join, zip(l1, l2)))навіть швидше, хоча не обов'язково більш пітонічне.
Інший спосіб:
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
Пітон 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'
''.join(itertools.chain.from_iterable(zip(u, l)))
zipзупиняється, коли коротший список був повністю повторений.
itertools.zip_longestможе використовуватися, якщо це стає проблемою.
На 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")
але до цього часу ви вже втратили надбавки над розрізанням списку для невеликих рядків (це все ще 20-кратна швидкість для довгих рядків), і це навіть не працює для символів, що не належать до 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_lolong :
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
list?? зайвий
"".join(list(...))будь ласка, 6.715280318699769, а час "".join(starmap(...))мені дасть 6.46332361384313
"".join(list(starmap(add, izip(l1,l2))))повільніше, ніж "".join(starmap(add, izip(l1,l2))). Я запускаю тест на своїй машині в python 2.7.11 та в python 3.5.1 навіть у віртуальній консолі www.python.org з python 3.4.3, і всі вони говорять те саме, і я запускаю її пару разів і завжди те саме
Ви також можете зробити це з допомогою 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:]])
Відчуває себе трохи непітонічним, щоб не враховувати відповідь на подвійний список розуміння тут, обробляти 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'
Як багато відповідей, найшвидше? Напевно, ні, але простий і гнучкий. Крім того, без надто великої складності, це трохи швидше, ніж прийнята відповідь (загалом додавання рядків у 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
Потенційно швидше і коротше, ніж поточне провідне рішення:
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*zip(u, l)))
Стратегія, що залежить від швидкості, полягає в тому, щоб зробити якомога більше на рівні С. Це ж виправлення zip_lo Long () для нерівних рядків, і воно виходитиме з того ж модуля, що і ланцюг (), тому я не можу набрати мені занадто багато очок там!
Інші рішення, які я придумав:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
Ви можете використовувати 1iteration_utilities.roundrobin
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.