Apa cara paling pythonic untuk menyatukan dua string?
Sebagai contoh:
Memasukkan:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Keluaran:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Apa cara paling pythonic untuk menyatukan dua string?
Sebagai contoh:
Memasukkan:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Keluaran:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Jawaban:
Bagi saya, cara paling pythonic * adalah yang berikut ini yang cukup banyak melakukan hal yang sama tetapi menggunakan +operator untuk menggabungkan karakter individu di setiap string:
res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Ini juga lebih cepat daripada menggunakan dua join()panggilan:
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
Ada pendekatan yang lebih cepat, tetapi mereka sering mengaburkan kode.
Catatan: Jika kedua string input tidak memiliki panjang yang sama maka string yang lebih panjang akan dipotong karena zipberhenti melakukan iterasi di ujung string yang lebih pendek. Dalam kasus ini, Anda zipharus menggunakan zip_longest( izip_longestdalam Python 2) dari itertoolsmodul untuk memastikan bahwa kedua string habis sepenuhnya.
* Untuk mengambil kutipan dari Zen of Python : Jumlah keterbacaan .
Pythonic = keterbacaan bagi saya; i + jhanya diurai secara visual dengan lebih mudah, setidaknya untuk mata saya.
"".join([i + j for i, j in zip(l1, l2)])dan itu pasti akan menjadi yang tercepat
"".join(map("".join, zip(l1, l2)))bahkan lebih cepat, meski belum tentu lebih pythonic.
Cara lain:
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))
Keluaran:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Sepertinya lebih cepat:
%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)
100000 loops, best of 3: 4.75 µs per loop
daripada solusi tercepat sejauh ini:
%timeit "".join(list(chain.from_iterable(zip(u, l))))
100000 loops, best of 3: 6.52 µs per loop
Juga untuk string yang lebih besar:
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()setara)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))
Keluaran:
AaBbCcDdEeFfGgHhIiJjKkLl
itertools.zip_longest(fillvalue='')setara)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))
Keluaran:
AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ
Dengan join()dan zip().
>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
''.join(itertools.chain.from_iterable(zip(u, l)))
zipberhenti ketika daftar yang lebih pendek telah diulangi sepenuhnya.
itertools.zip_longestdapat digunakan jika menjadi masalah.
Pada Python 2, dengan jauh cara yang lebih cepat untuk melakukan hal-hal, di ~ 3x kecepatan daftar mengiris untuk string kecil dan ~ 30x untuk yang panjang, adalah
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
Ini tidak akan berfungsi pada Python 3. Anda bisa menerapkan sesuatu seperti
res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")
tetapi saat itu Anda sudah kehilangan keuntungan atas pemotongan daftar untuk string kecil (kecepatannya masih 20x untuk string panjang) dan ini bahkan belum berfungsi untuk karakter non-ASCII.
FWIW, jika Anda sedang melakukan hal ini pada string besar dan perlu setiap siklus, dan untuk beberapa alasan harus menggunakan Python string ... berikut adalah cara untuk melakukannya:
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")
Casing khusus, casing umum dari jenis yang lebih kecil juga akan membantu. FWIW, ini hanya 3x kecepatan pemotongan daftar untuk string panjang dan faktor 4 hingga 5 lebih lambat untuk string kecil.
Bagaimanapun saya lebih suka joinsolusinya, tetapi karena pengaturan waktu disebutkan di tempat lain, saya pikir saya sebaiknya bergabung.
Jika Anda menginginkan cara tercepat, Anda dapat menggabungkan itertools dengan 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
Tapi menggabungkan izipdan chain.from_iterablelebih cepat lagi
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
Ada juga perbedaan substansial antara
chain(*dan chain.from_iterable(....
In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop
Tidak ada yang namanya generator dengan join, melewatkan satu akan selalu lebih lambat karena python pertama-tama akan membuat daftar menggunakan konten karena ia melakukan dua melewati data, satu untuk mengetahui ukuran yang dibutuhkan dan satu untuk benar-benar melakukannya gabungan yang tidak mungkin dilakukan menggunakan generator:
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.
*/
Juga jika Anda memiliki panjang string yang berbeda dan Anda tidak ingin kehilangan data, Anda dapat menggunakan 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'
Untuk python 3 itu disebut zip_longest
Tetapi untuk python2, saran veedrac sejauh ini adalah yang tercepat:
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?? tidak dibutuhkan
"".join(list(...))memberi saya 6.715280318699769 dan timeit "".join(starmap(...))memberi saya 6.46332361384313
"".join(list(starmap(add, izip(l1,l2))))lebih lambat dari "".join(starmap(add, izip(l1,l2))). Saya menjalankan tes di mesin saya di python 2.7.11 dan di python 3.5.1 bahkan di konsol virtual www.python.org dengan python 3.4.3 dan semua mengatakan hal yang sama dan saya menjalankannya beberapa kali dan selalu sama
Anda juga bisa melakukan ini dengan menggunakan mapdan operator.add:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
Keluaran :
'AaAaAaAaAa'
Apa yang dilakukan map adalah mengambil setiap elemen dari iterable pertama udan elemen pertama dari iterable kedua ldan menerapkan fungsi yang disediakan sebagai argumen pertama add. Kemudian bergabunglah bergabung saja dengan mereka.
Banyak dari saran ini mengasumsikan string memiliki panjang yang sama. Mungkin itu mencakup semua kasus penggunaan yang wajar, tetapi setidaknya bagi saya tampaknya Anda mungkin ingin mengakomodasi string dengan panjang yang berbeda juga. Atau apakah saya satu-satunya yang berpikir mesh harus bekerja seperti ini:
u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
Salah satu cara untuk melakukannya adalah sebagai berikut:
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:]])
Saya suka menggunakan dua fors, nama variabel dapat memberikan petunjuk / pengingat tentang apa yang sedang terjadi:
"".join(char for pair in zip(u,l) for char in pair)
Terasa agak tidak pythonic untuk tidak mempertimbangkan jawaban pemahaman daftar ganda di sini, untuk menangani string n dengan usaha O (1):
"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
di mana all_stringsdaftar string yang ingin Anda sisipkan. Dalam kasus Anda all_strings = [u, l],. Contoh penggunaan lengkap akan terlihat seperti ini:
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'
Seperti banyak jawaban, tercepat? Mungkin tidak, tapi sederhana dan fleksibel. Juga, tanpa terlalu banyak kerumitan tambahan, ini sedikit lebih cepat daripada jawaban yang diterima (secara umum, penambahan string agak lambat di 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
Berpotensi lebih cepat dan lebih pendek dari solusi terdepan saat ini:
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*zip(u, l)))
Strategi yang bijak dengan kecepatan adalah melakukan sebanyak mungkin pada level C. Zip_longest () yang sama untuk string yang tidak rata dan itu akan keluar dari modul yang sama dengan chain () jadi tidak bisa memberi saya terlalu banyak poin di sana!
Solusi lain yang saya temukan selama ini:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
Anda bisa menggunakan 1iteration_utilities.roundrobin
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
from iteration_utilities import roundrobin
''.join(roundrobin(u, l))
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
atau ManyIterableskelas dari paket yang sama:
from iteration_utilities import ManyIterables
ManyIterables(u, l).roundrobin().as_string()
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
1 ini adalah dari pihak ketiga perpustakaan saya telah menulis: iteration_utilities.