¿Cuál es la forma más pitónica de unir dos cuerdas?
Por ejemplo:
Entrada:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Salida:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
fuente
¿Cuál es la forma más pitónica de unir dos cuerdas?
Por ejemplo:
Entrada:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Salida:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Para mí, la forma más pitónica * es la siguiente, que prácticamente hace lo mismo pero usa el +
operador para concatenar los caracteres individuales en cada cadena:
res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
También es más rápido que usar dos join()
llamadas:
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
Existen enfoques más rápidos, pero a menudo confunden el código.
Nota: Si las dos cadenas de entrada no tienen la misma longitud, la más larga se truncará cuando zip
deje de iterar al final de la cadena más corta. En este caso, en lugar de zip
uno, debería usar zip_longest
( izip_longest
en Python 2) del itertools
módulo para asegurarse de que ambas cadenas se agoten por completo.
* Para tomar una cita del Zen de Python : la legibilidad cuenta .
Pythonic = legibilidad para mí; i + j
se analiza visualmente más fácilmente, al menos para mis ojos.
"".join([i + j for i, j in zip(l1, l2)])
y definitivamente será el más rápido
"".join(map("".join, zip(l1, l2)))
es incluso más rápido, aunque no necesariamente más pitónico.
De otra manera:
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))
Salida:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Parece que es más rápido:
%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)
100000 loops, best of 3: 4.75 µs per loop
que la solución más rápida hasta ahora:
%timeit "".join(list(chain.from_iterable(zip(u, l))))
100000 loops, best of 3: 6.52 µs per loop
También para las cuerdas más grandes:
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))
Salida:
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))
Salida:
AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ
Con join()
y zip()
.
>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
''.join(itertools.chain.from_iterable(zip(u, l)))
zip
detiene cuando la lista más corta se ha iterado por completo.
itertools.zip_longest
se puede utilizar si se convierte en un problema.
En Python 2, con mucho, la forma más rápida de hacer las cosas, a ~ 3 veces la velocidad de corte de la lista para cadenas pequeñas y ~ 30x para cadenas largas, es
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
Sin embargo, esto no funcionaría en Python 3. Podrías implementar algo como
res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")
pero para entonces ya ha perdido las ganancias sobre la división de listas para cadenas pequeñas (sigue siendo 20 veces la velocidad para cadenas largas) y esto ni siquiera funciona para caracteres que no son ASCII todavía.
FWIW, si está haciendo esto en cadenas masivas y necesita cada ciclo, y por alguna razón tiene que usar cadenas de Python ... así es como se hace:
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")
La carcasa especial, el caso común de tipos más pequeños, también ayudará. FWIW, esto es solo 3 veces la velocidad de corte de listas para cadenas largas y un factor de 4 a 5 más lento para cadenas pequeñas.
De cualquier manera, prefiero las join
soluciones, pero como los tiempos se mencionaron en otra parte, pensé que también podría unirme.
Si desea la forma más rápida, puede combinar 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
Pero combinando izip
y chain.from_iterable
es más rápido de nuevo
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
También hay una diferencia sustancial entre
chain(*
y chain.from_iterable(...
.
In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop
No existe un generador con combinación, pasar uno siempre será más lento, ya que Python primero creará una lista utilizando el contenido porque hace dos pasadas sobre los datos, una para determinar el tamaño necesario y otra para hacer realmente la unión que no sería posible usando un generador:
unirse.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.
*/
Además, si tiene cadenas de diferente longitud y no desea perder datos, puede usar 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'
Para python 3 se llama zip_longest
Pero para python2, la sugerencia de veedrac es, con mucho, la más rápida:
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
?? es innecesario
"".join(list(...))
dame 6.715280318699769 y timeit the "".join(starmap(...))
dame 6.46332361384313
"".join(list(starmap(add, izip(l1,l2))))
es más lento que "".join(starmap(add, izip(l1,l2)))
. Ejecuto la prueba en mi máquina en python 2.7.11 y en python 3.5.1 incluso en la consola virtual de www.python.org con python 3.4.3 y todos dicen lo mismo y lo ejecuto un par de veces y siempre el mismo
También puede hacer esto usando map
y operator.add
:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
Salida :
'AaAaAaAaAa'
Lo que hace el mapa es que toma todos los elementos del primer iterable u
y los primeros elementos del segundo iterable l
y aplica la función proporcionada como primer argumento add
. Entonces únete solo se une a ellos.
La respuesta de Jim es excelente, pero esta es mi opción favorita, si no le importan un par de importaciones:
from functools import reduce
from operator import add
reduce(add, map(add, u, l))
Muchas de estas sugerencias asumen que las cadenas tienen la misma longitud. Tal vez eso cubra todos los casos de uso razonables, pero al menos a mí me parece que es posible que también desee acomodar cadenas de diferentes longitudes. ¿O soy el único que piensa que la malla debería funcionar un poco así:
u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
Una forma de hacerlo sería la siguiente:
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:]])
Me gusta usar dos for
s, los nombres de las variables pueden dar una pista / recordatorio de lo que está sucediendo:
"".join(char for pair in zip(u,l) for char in pair)
Solo para agregar otro enfoque más básico:
st = ""
for char in u:
st = "{0}{1}{2}".format( st, char, l[ u.index( char ) ] )
Se siente un poco poco pitónico no considerar la respuesta de comprensión de doble lista aquí, para manejar n cadena con O (1) esfuerzo:
"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
donde all_strings
es una lista de las cadenas que desea intercalar. En tu caso all_strings = [u, l]
,. Un ejemplo de uso completo se vería así:
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'
Como muchas respuestas, ¿más rápido? Probablemente no, pero simple y flexible. Además, sin demasiada complejidad adicional, esto es un poco más rápido que la respuesta aceptada (en general, la adición de cadenas es un poco lenta en 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
Potencialmente más rápido y más corto que la solución líder actual:
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*zip(u, l)))
La estrategia en cuanto a velocidad es hacer todo lo posible en el nivel C. La misma solución zip_longest () para cadenas desiguales y saldría del mismo módulo que chain (), ¡así que no puedo marcarme demasiados puntos allí!
Otras soluciones que se me ocurrieron en el camino:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
Podrías usar 1iteration_utilities.roundrobin
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
from iteration_utilities import roundrobin
''.join(roundrobin(u, l))
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
o la ManyIterables
clase del mismo paquete:
from iteration_utilities import ManyIterables
ManyIterables(u, l).roundrobin().as_string()
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
1 Se trata de una biblioteca de terceros que he escrito: iteration_utilities
.
Usaría zip () para obtener una manera fácil y legible:
result = ''
for cha, chb in zip(u, l):
result += '%s%s' % (cha, chb)
print result
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'