Qual è il modo più pitonico per unire due corde insieme?
Per esempio:
Ingresso:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Produzione:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Qual è il modo più pitonico per unire due corde insieme?
Per esempio:
Ingresso:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Produzione:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Risposte:
Per me, il modo più pitonico * è il seguente che praticamente fa la stessa cosa ma utilizza l' +
operatore per concatenare i singoli caratteri in ogni stringa:
res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
È anche più veloce rispetto all'utilizzo di due join()
chiamate:
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
Esistono approcci più rapidi, ma spesso offuscano il codice.
Nota: se le due stringhe di input non hanno la stessa lunghezza, quella più lunga verrà troncata poiché zip
interrompe l'iterazione alla fine della stringa più corta. In questo caso invece di zip
uno dovrebbe usare zip_longest
( izip_longest
in Python 2) dal itertools
modulo per assicurarsi che entrambe le stringhe siano completamente esaurite.
* Per prendere una citazione dallo Zen di Python : la leggibilità conta .
Pythonic = leggibilità per me; i + j
è solo visivamente analizzato più facilmente, almeno per i miei occhi.
"".join([i + j for i, j in zip(l1, l2)])
e sarà sicuramente il più veloce
"".join(map("".join, zip(l1, l2)))
è ancora più veloce, anche se non necessariamente più pitonico.
Un altro modo:
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))
Produzione:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Sembra che sia più veloce:
%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)
100000 loops, best of 3: 4.75 µs per loop
rispetto alla soluzione più veloce finora:
%timeit "".join(list(chain.from_iterable(zip(u, l))))
100000 loops, best of 3: 6.52 µs per loop
Anche per le corde più grandi:
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()
equivalente)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))
Produzione:
AaBbCcDdEeFfGgHhIiJjKkLl
itertools.zip_longest(fillvalue='')
equivalente)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))
Produzione:
AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ
Con join()
e zip()
.
>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
''.join(itertools.chain.from_iterable(zip(u, l)))
zip
interrompe quando l'elenco più breve è stato completamente iterato.
itertools.zip_longest
può essere utilizzato se diventa un problema.
Su Python 2, di gran lunga il modo più veloce di fare le cose, a ~ 3 volte la velocità di suddivisione della lista per stringhe piccole e ~ 30x per stringhe lunghe, è
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
Tuttavia, questo non funzionerebbe su Python 3. Potresti implementare qualcosa di simile
res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")
ma a quel punto hai già perso i guadagni rispetto all'affettamento della lista per stringhe piccole (è ancora 20 volte la velocità per stringhe lunghe) e questo non funziona ancora per i caratteri non ASCII.
FWIW, se lo stai facendo su stringhe enormi e hai bisogno di ogni ciclo, e per qualche motivo devi usare stringhe Python ... ecco come farlo:
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")
Anche l'involucro speciale del caso comune di tipi più piccoli aiuterà. FWIW, questa è solo 3 volte la velocità di suddivisione della lista per stringhe lunghe e un fattore da 4 a 5 più lento per stringhe piccole.
Ad ogni modo preferisco le join
soluzioni, ma poiché i tempi sono stati menzionati altrove, ho pensato che avrei potuto anche partecipare.
Se vuoi il modo più veloce, puoi combinare itertools con 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
Ma combinando izip
ed chain.from_iterable
è di nuovo più veloce
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
C'è anche una differenza sostanziale tra
chain(*
e chain.from_iterable(...
.
In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop
Non esiste un generatore con join, passarne uno sarà sempre più lento poiché python costruirà prima un elenco utilizzando il contenuto perché esegue due passaggi sui dati, uno per capire la dimensione necessaria e uno per farlo effettivamente il join che non sarebbe possibile utilizzando un generatore:
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.
*/
Inoltre, se hai stringhe di lunghezza diversa e non vuoi perdere dati, puoi usare 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'
Per python 3 si chiama zip_longest
Ma per python2, il suggerimento di veedrac è di gran lunga il più veloce:
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
?? non è necessario
"".join(list(...))
give me 6.715280318699769 and timeit the "".join(starmap(...))
give me 6.46332361384313
"".join(list(starmap(add, izip(l1,l2))))
è più lento di "".join(starmap(add, izip(l1,l2)))
. Eseguo il test sulla mia macchina in python 2.7.11 e in python 3.5.1 anche nella console virtuale di www.python.org con python 3.4.3 e dicono tutti la stessa cosa e lo eseguo un paio di volte e sempre il stesso
Puoi anche farlo usando map
e operator.add
:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
Uscita :
'AaAaAaAaAa'
Quello che fa la mappa è che prende ogni elemento dal primo iterabile u
e i primi elementi dal secondo iterabile l
e applica la funzione fornita come primo argomento add
. Quindi unisciti a loro.
Molti di questi suggerimenti presuppongono che le stringhe abbiano la stessa lunghezza. Forse questo copre tutti i casi d'uso ragionevoli, ma almeno a me sembra che potresti voler ospitare anche stringhe di lunghezze diverse. O sono l'unico a pensare che la mesh dovrebbe funzionare un po 'così:
u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
Un modo per farlo sarebbe il seguente:
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:]])
Mi piace usare due for
s, i nomi delle variabili possono dare un suggerimento / promemoria su cosa sta succedendo:
"".join(char for pair in zip(u,l) for char in pair)
Sembra un po 'poco pitonico non considerare la risposta di comprensione della doppia lista qui, per gestire n string con O (1) sforzo:
"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
dove all_strings
è un elenco delle stringhe che vuoi intercalare. Nel tuo caso all_strings = [u, l]
,. Un esempio di utilizzo completo sarebbe simile a questo:
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'
Come molte risposte, più veloce? Probabilmente no, ma semplice e flessibile. Inoltre, senza troppa complessità aggiunta, questo è leggermente più veloce della risposta accettata (in generale, l'aggiunta di stringhe è un po 'lenta in 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
Potenzialmente più veloce e più breve dell'attuale soluzione leader:
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*zip(u, l)))
Per quanto riguarda la velocità, la strategia è fare il più possibile a livello C. Stessa correzione di zip_longest () per stringhe irregolari e sarebbe uscito dallo stesso modulo di chain () quindi non posso farmi troppi punti lì!
Altre soluzioni che ho trovato lungo la strada:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
Potresti usare 1iteration_utilities.roundrobin
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
from iteration_utilities import roundrobin
''.join(roundrobin(u, l))
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
o la ManyIterables
classe dallo stesso pacchetto:
from iteration_utilities import ManyIterables
ManyIterables(u, l).roundrobin().as_string()
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
1 Questo è da una libreria di terze parti che ho scritto: iteration_utilities
.