La función zip() apareció por primera vez en Python 2 (entonces devolvía una lista), y a partir de Python 3 devuelve un iterador perezoso. "Une" varias secuencias en tuplas elemento por elemento, lo que ha hecho que el procesamiento de colecciones iterables paralelas sea cómodo y eficiente.
A menudo es necesario procesar varias listas (u otros tipos de secuencias) al mismo tiempo: por ejemplo, iterar sobre un par clave-valor o procesar coordenadas punto-par. La sincronización manual de índices es fuente de errores y dificulta la legibilidad del código, especialmente para colecciones de diferente longitud.
La función zip() acepta cualquier cantidad de objetos iterables y devuelve un iterador de tuplas, en cada uno de los cuales se toman los elementos correspondientes de cada iterable. Si las secuencias son de diferente longitud, el resultado se corta en la más corta.
names = ['Alice', 'Bob', 'Charlie'] ages = [24, 27, 30] for name, age in zip(names, ages): print(f'{name} tiene {age} años')
Se puede descomprimir zip usando *:
pairs = [(1, 'a'), (2, 'b'), (3, 'c')] nums, chars = zip(*pairs) print(nums) # (1, 2, 3) print(chars) # ('a', 'b', 'c')
¿Qué pasará si se pasa a zip() colecciones de diferente longitud?
zip() se detendrá cuando alcance el final de la colección más corta; los demás elementos de las colecciones largas se ignorarán.
print(list(zip([1,2,3], ['a','b']))) # [(1, 'a'), (2, 'b')]
¿Cómo obtener tuplas, completando las secuencias más cortas con un valor por defecto?
La zip() estándar no puede hacer esto, pero existe itertools.zip_longest para tal comportamiento:
from itertools import zip_longest for a, b in zip_longest([1,2], ['x','y','z'], fillvalue=None): print(a, b) # 1 x # 2 y # None z
¿Se puede "desempaquetar" el resultado de zip(), para volver a obtener las listas originales?
Sí, si todas las colecciones originales tenían la misma longitud y el resultado no ha sido alterado, el operador * permite descomprimir zip.
pairs = [(1,2), (3,4)] a, b = zip(*pairs) print(a) # (1, 3) print(b) # (2, 4)
Procesamiento de colecciones relacionadas de diferente longitud, sin tener en cuenta las particularidades de zip:
lst1 = [1,2,3,4] lst2 = ['a','b'] for x, y in zip(lst1, lst2): print(x, y) # 1 a # 2 b # (3,4) y 'c', 'd' de lst1 no se procesaron
Ventajas:
Desventajas:
Uso de zip_longest con fillvalue, para no perder ningún elemento:
from itertools import zip_longest lst1 = [1,2,3,4] lst2 = ['a','b'] for x, y in zip_longest(lst1, lst2, fillvalue='?'): print(x, y) # 1 a # 2 b # 3 ? # 4 ?
Ventajas:
Desventajas: