La forma más python de intercalar dos cuerdas

¿Cuál es la forma más python de unir dos cuerdas?

Por ejemplo:

Entrada:

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijklmnopqrstuvwxyz' 

Salida:

 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

Para mí, la forma más pythonic * es la siguiente que hace prácticamente 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 llamadas 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 

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á, ya que el zip deja de iterar al final de la cadena más corta. En este caso, en lugar de zip debe usar zip_longest ( izip_longest en Python 2) del módulo itertools para asegurarse de que ambas cadenas están completamente agotadas.


* 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.

Alternativa mas rapida

De otra manera:

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

Salida:

 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz' 

Velocidad

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.

Variación para cuerdas con diferentes longitudes.

 u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' l = 'abcdefghijkl' 

Una más corta determina la longitud ( 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 

Una más larga determina la longitud ( 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' 

En Python 2, con mucho, la forma más rápida de hacer las cosas, a ~ 3x la velocidad de corte de la lista para cadenas pequeñas y ~ 30x para las 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 el corte de la lista para cadenas pequeñas (todavía es 20 veces la velocidad para cadenas largas) y esto aún no funciona para los caracteres que no son ASCII.

FWIW, si está haciendo esto en cadenas masivas y necesita cada ciclo, y por alguna razón tiene que usar cadenas de Python … aquí le indicamos cómo hacerlo:

 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") 

Carcasa especial el caso común de tipos más pequeños ayudará también. FWIW, esto es solo 3 veces la velocidad de corte de lista para cadenas largas y un factor de 4 a 5 más lento para cadenas pequeñas.

De cualquier manera, prefiero las soluciones de join , pero como los tiempos se mencionaron en otra parte, pensé que también podría participar.

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 combinar chain.from_iterable y chain.from_iterable vuelve a ser más rápido

 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 tal cosa como un generador con unión, pasar uno siempre será más lento, ya que Python primero creará una lista con el contenido porque hace dos pases sobre los datos, uno para averiguar el tamaño necesario y el otro para hacerlo. la unión que no sería posible utilizando un generador:

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. */ 

Además, si tiene cadenas de longitud diferente 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 

También puedes 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 de la primera iterable u y los primeros elementos de la segunda iterable y aplica la función suministrada como el primer argumento add . Entonces únete solo únete a ellos.

La respuesta de Jim es genial, pero aquí está mi opción favorita, si no te importa 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 cuerdas son de igual longitud. Tal vez eso cubra todos los casos de uso razonables, pero al menos para mí, parece que es posible que desee acomodar cadenas de diferentes longitudes también. 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 hacer esto 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 sugerencia / 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 ) ] ) 

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 la velocidad es hacer tanto como sea posible en el nivel-C. La misma corrección zip_longest () para cadenas desiguales y saldría del mismo módulo que chain (), ¡así que no puedo darme demasiados puntos allí!

Otras soluciones que encontré 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)) 

Se siente un poco untípico al no considerar la respuesta de doble lista de comprensión aquí, para manejar n cuerdas con esfuerzo O (1):

 "".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 su 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, mas rapido? Probablemente no, pero simple y flexible. Además, sin demasiada complejidad agregada, 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 

Podrías usar iteration_utilities.roundrobin 1

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

o la clase ManyIterables del mismo paquete:

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

1 Esto es de una biblioteca de terceros que he escrito: iteration_utilities .

Usaría zip () para obtener una forma legible y fácil:

 result = '' for cha, chb in zip(u, l): result += '%s%s' % (cha, chb) print result # 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'