La función zip() es una de las herramientas más útiles y subestimadas de Python. Permite combinar dos o más iterables en pares, devolviendo un iterador que agrupa los elementos de cada secuencia. Si alguna vez necesitaste recorrer dos listas al mismo tiempo o transponer columnas en filas, zip() es la solución ideal.
En esta guía completa aprenderás desde lo básico hasta técnicas avanzadas con ejemplos prácticos y probados. Cubriremos la sintaxis, los parámetros, el modo strict (novedad de Python 3.10), la descompresión con zip(*), la creación de diccionarios y mucho más.
¿Qué es la función zip() de Python?
La función zip() recibe uno o más iterables (listas, tuplas, cadenas, etc.) y devuelve un iterador de tuplas, donde cada tupla contiene un elemento de cada iterable original. El nombre zip viene de la metáfora del cierre relámpago: los dientes de ambos lados se ensamblan uno a uno.
Esta es la sintaxis básica:
zip(*iterables, strict=False)
El parámetro strict se introdujo en Python 3.10. Cuando está activado, lanza un ValueError si los iterables tienen longitudes diferentes. Por defecto, zip() se detiene en el iterable más corto.
Fuente: Documentación oficial de Python — zip()
Cómo usar zip() en la práctica
Empecemos con el ejemplo más simple: unir dos listas del mismo tamaño.
nombres = ['Ana', 'Bruno', 'Carlos']
edades = [25, 32, 28]
pares = list(zip(nombres, edades))
print(pares)
# [('Ana', 25), ('Bruno', 32), ('Carlos', 28)]
Cada tupla combina el nombre con la edad correspondiente. Nota que usamos list() para materializar el iterador; sin él, zip() devuelve un objeto zip que se puede consumir bajo demanda.
Para iterar directamente:
for nombre, edad in zip(nombres, edades):
print(f'{nombre} tiene {edad} años')
# Ana tiene 25 años
# Bruno tiene 32 años
# Carlos tiene 28 años
Puedes pasar tantos iterables como quieras. Con tres listas:
productos = ['Ratón', 'Teclado', 'Monitor']
precios = [50, 120, 800]
cantidades = [10, 5, 3]
for producto, precio, cant in zip(productos, precios, cantidades):
total = precio * cant
print(f'{producto}: ${total:.2f}')
# Ratón: $500.00
# Teclado: $600.00
# Monitor: $2400.00
Fuente: Real Python — Using the Python zip() Function
Comportamiento con iterables de diferentes longitudes
Por defecto, zip() deja de combinar cuando el iterable más corto se agota. Esto se llama comportamiento de truncamiento.
a = [1, 2, 3, 4, 5]
b = ['a', 'b', 'c']
resultado = list(zip(a, b))
print(resultado)
# [(1, 'a'), (2, 'b'), (3, 'c')]
Los valores 4 y 5 de la lista a se ignoran porque b solo tiene 3 elementos.
Modo strict (Python 3.10+)
Si quieres garantizar que todos los iterables tengan la misma longitud, usa strict=True:
try:
resultado = list(zip(a, b, strict=True))
except ValueError as e:
print(e) # zip() argument 2 is shorter than argument 1
Esto es útil para evitar errores silenciosos en tuberías de datos, especialmente cuando los datos provienen de fuentes externas.
Fuente: GeeksforGeeks — zip() in Python
Descomprimir con zip(*)
El operador * invierte el zip(): "descomprime" una lista de tuplas de vuelta a tuplas separadas. Este patrón es extremadamente común.
pares = [('Ana', 25), ('Bruno', 32), ('Carlos', 28)]
nombres, edades = zip(*pares)
print(nombres) # ('Ana', 'Bruno', 'Carlos')
print(edades) # (25, 32, 28)
En la práctica, esto equivale a transponer datos: las "columnas" se convierten en "filas" y viceversa.
datos = [(1, 'a', True), (2, 'b', False), (3, 'c', True)]
col1, col2, col3 = zip(*datos)
print(col1) # (1, 2, 3)
print(col2) # ('a', 'b', 'c')
print(col3) # (True, False, True)
Este patrón se usa mucho en análisis de datos y manipulación de matrices.
Fuente: Stack Overflow — Transpose/Unzip in Python
Crear diccionarios con zip()
Una de las aplicaciones más comunes de zip() es construir diccionarios combinando llaves y valores:
llaves = ['nombre', 'edad', 'ciudad']
valores = ['Ana', 25, 'Madrid']
persona = dict(zip(llaves, valores))
print(persona)
# {'nombre': 'Ana', 'edad': 25, 'ciudad': 'Madrid'}
Puedes ir más allá y crear una lista de diccionarios fácilmente:
campos = ['nombre', 'edad', 'ciudad']
datos = [
['Ana', 25, 'Madrid'],
['Bruno', 32, 'Barcelona'],
['Carlos', 28, 'Valencia']
]
personas = [dict(zip(campos, registro)) for registro in datos]
print(personas)
# [
# {'nombre': 'Ana', 'edad': 25, 'ciudad': 'Madrid'},
# {'nombre': 'Bruno', 'edad': 32, 'ciudad': 'Barcelona'},
# {'nombre': 'Carlos', 'edad': 28, 'ciudad': 'Valencia'}
# ]
Si quieres profundizar en listas y manipulación de datos, consulta nuestra guía completa sobre listas en Python. Para más información sobre diccionarios, visita la guía completa de diccionarios en Python.
Fuente: Programiz — Python zip() Built-in Function
Iteración paralela con zip() en bucles
El caso de uso más frecuente de zip() es iterar sobre múltiples secuencias simultáneamente, evitando el acceso basado en índices.
estudiantes = ['Ana', 'Bruno', 'Carlos', 'Daniel']
notas = [8.5, 7.2, 9.0, 6.8]
aprobados = [True, True, True, False]
for estudiante, nota, aprobado in zip(estudiantes, notas, aprobados):
estado = 'aprobado' if aprobado else 'reprobado'
print(f'{estudiante} sacó {nota} — {estado}')
Sin zip(), necesitarías range(len(estudiantes)) y acceder a cada lista por índice, lo cual es más verboso y propenso a errores.
Comparación con enumerate
Usa zip() cuando las listas son independientes y quieres emparejarlas. Usa enumerate() cuando necesitas el índice de los elementos de una sola lista.
# zip para pares de listas
for nombre, edad in zip(nombres, edades):
...
# enumerate para índice + elemento
for i, nombre in enumerate(nombres):
...
Fuente: W3Schools — Python zip() Function
zip() con cadenas, tuplas y otros iterables
zip() funciona con cualquier iterable, no solo listas. Las cadenas, tuplas, conjuntos y generadores son válidos.
# Con cadenas
letras = zip('ABC', '123')
print(list(letras)) # [('A', '1'), ('B', '2'), ('C', '3')]
# Con tuplas
t1 = (1, 2, 3)
t2 = (4, 5, 6)
print(list(zip(t1, t2))) # [(1, 4), (2, 5), (3, 6)]
# Con generator expressions
pares_impares = zip(
(x for x in range(0, 10, 2)),
(x for x in range(1, 10, 2))
)
print(list(pares_impares))
# [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
Fuente: Tutorial de Python — Estructuras de Datos
zip() vacío y con un solo iterable
Si no pasas ningún argumento, zip() devuelve un iterador vacío:
vacio = list(zip())
print(vacio) # []
Con un solo iterable, zip() devuelve tuplas de un elemento:
unico = list(zip(['a', 'b', 'c']))
print(unico) # [('a',), ('b',), ('c',)]
Esto puede ser útil cuando quieres estandarizar la salida de una función que a veces recibe uno y a veces varios iterables.
zip() vs itertools.zip_longest()
Mientras que zip() trunca al iterable más corto, itertools.zip_longest() continúa hasta el más largo, rellenando los valores faltantes con un fillvalue (por defecto None).
from itertools import zip_longest
a = [1, 2, 3, 4]
b = ['a', 'b']
print(list(zip(a, b)))
# [(1, 'a'), (2, 'b')]
print(list(zip_longest(a, b)))
# [(1, 'a'), (2, 'b'), (3, None), (4, None)]
print(list(zip_longest(a, b, fillvalue='X')))
# [(1, 'a'), (2, 'b'), (3, 'X'), (4, 'X')]
Elige zip() cuando quieres que los datos estén alineados perfectamente, y zip_longest() cuando necesitas preservar todos los elementos aunque algunas secuencias sean más cortas.
Fuente: Python itertools — zip_longest()
Ejemplos reales con zip()
1. Transposición de matriz
matriz = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
transpuesta = list(zip(*matriz))
print(transpuesta)
# [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
2. Diccionario desde dos listas
estudiantes = ['Ana', 'Bruno', 'Carlos']
notas_finales = [9.0, 7.5, 8.8]
boletin = dict(zip(estudiantes, notas_finales))
print(boletin) # {'Ana': 9.0, 'Bruno': 7.5, 'Carlos': 8.8}
3. Agrupar datos en pares consecutivos
def agrupar_en_pares(iterable):
it = iter(iterable)
return list(zip(it, it))
print(agrupar_en_pares([1, 2, 3, 4, 5, 6]))
# [(1, 2), (3, 4), (5, 6)]
4. Ordenar listas relacionadas juntas
nombres = ['Carlos', 'Ana', 'Bruno']
edades = [28, 25, 32]
# Ordenar edades siguiendo el orden de los nombres
pares_ordenados = sorted(zip(nombres, edades))
nombres_ordenados, edades_ordenadas = zip(*pares_ordenados)
print(list(nombres_ordenados)) # ['Ana', 'Bruno', 'Carlos']
print(list(edades_ordenadas)) # (25, 32, 28)
Fuente: NumPy — Guía para Principiantes
Rendimiento: zip() vs bucles con índices
zip() no solo es más legible — también es más eficiente. Comparemos el rendimiento con timeit:
import timeit
a = list(range(1000000))
b = list(range(1000000))
# Con zip()
tiempo_zip = timeit.timeit(
'[(x, y) for x, y in zip(a, b)]',
globals={'a': a, 'b': b},
number=100
)
# Con índices
tiempo_indices = timeit.timeit(
'[(a[i], b[i]) for i in range(len(a))]',
globals={'a': a, 'b': b},
number=100
)
print(f'zip(): {tiempo_zip:.3f}s')
print(f'índices: {tiempo_indices:.3f}s')
En la práctica, zip() suele ser más rápido porque delega la iteración a la implementación interna optimizada en C de Python, mientras que los bucles con índices implican más operaciones en la capa de Python.
Errores comunes con zip()
Olvidar materializar el iterador
z = zip([1, 2], ['a', 'b'])
print(z) # <zip object at 0x...> — ¡no es lo que querías!
print(list(z)) # [(1, 'a'), (2, 'b')]
Confundir zip con zip_longest
Si no sabes que zip() trunca al iterable más corto, podrías perder datos silenciosamente. Siempre que la longitud de los iterables sea incierta, considera usar zip_longest() o validar con strict=True.
Modificar listas mientras se itera
a = [1, 2, 3]
b = ['a', 'b', 'c']
for x, y in zip(a, b):
a.append(x * 10) # ¡Esto crea un bucle infinito!
Itera siempre sobre copias o colecciones separadas si necesitas modificar la original.
Fuente: Real Python — zip() in Practice
Buenas prácticas
- Prefiere zip() sobre índices: siempre que te sientas tentado a escribir
for i in range(len(lista))para acceder a dos listas, usazip(). - Usa desempaquetado: en lugar de
for par in zip(a, b): x, y = par, escribefor x, y in zip(a, b)directamente. - Activa strict en validaciones: cuando los datos deben tener la misma longitud, pasa
strict=Truepara evitar errores silenciosos. - Combínalo con sorted(): usa
sorted(zip(...))para ordenar múltiples listas de forma coordinada. - No abuses de zip(): para procesar una sola secuencia,
enumerate()o un bucle simple son más adecuados.
Conclusión
La función zip() es una herramienta indispensable en el día a día de cualquier desarrollador Python. Simplifica la iteración paralela, la creación de diccionarios, la transposición de matrices y el emparejamiento de datos en general.
Dominar zip() — y saber cuándo usar zip_longest(), strict=True y la descompresión con * — es un paso importante para escribir código Python más idiomático, legible y eficiente.
Sigue explorando el Universo Python con nuestras guías gratuitas: aprende a trabajar con listas en Python y diccionarios en Python para complementar tu aprendizaje sobre manipulación de datos.