Estos dos van primero porque los escribes todos los días, y porque lo último que hace zip en este post es justo la pregunta que responde el resto de la serie.
El contador que sigues escribiendo
names = ['ada', 'grace', 'alan']
i = 0
for name in names:
print(i, name)
i += 1
Imprime:
0 ada
1 grace
2 alan
Tres líneas de contabilidad alrededor de una línea de trabajo real. enumerate se encarga de todas:
names = ['ada', 'grace', 'alan']
for i, name in enumerate(names):
print(i, name)
Imprime:
0 ada
1 grace
2 alan
enumerate te entrega un par en cada vuelta: el conteo y después el elemento.
start cambia el número, no la posición
names = ['ada', 'grace', 'alan']
for n, name in enumerate(names, start=1):
print(f"{n}. {name}")
Imprime:
1. ada
2. grace
3. alan
Este es el que la gente busca y luego termina escribiendo i + 1 en su lugar. start no se salta nada: solo cambia el primer número que se reparte.
Deja de indexar a mano
names = ['ada', 'grace', 'alan']
for i in range(len(names)): # you index on every line that uses the item
print(i, names[i].upper())
for i, name in enumerate(names): # the item is already unpacked
print(i, name.upper())
Imprime:
0 ADA
1 GRACE
2 ALAN
0 ADA
1 GRACE
2 ALAN
La misma salida, y la segunda versión no puede equivocarse con el índice. Vale la pena tratar range(len(x)) como una pequeña alarma: casi siempre significa enumerate, y a veces significa que nunca necesitaste el índice.
zip recorre dos secuencias a la vez
names = ['ada', 'grace', 'alan']
langs = ['analytical engine', 'cobol', 'turing machine']
for name, lang in zip(names, langs):
print(f"{name:6} {lang}")
Imprime:
ada analytical engine
grace cobol
alan turing machine
zip se detiene en la entrada más corta y no dice nada
Este es el que le cuesta una tarde a mucha gente.
names = ['ada', 'grace', 'alan']
scores = [90, 85]
pairs = list(zip(names, scores))
print(pairs)
print(len(names), len(scores), '->', len(pairs))
Imprime:
[('ada', 90), ('grace', 85)]
3 2 -> 2
Alan desapareció. Sin error, sin advertencia: solo dos filas donde esperabas tres. Si se supone que las dos listas tienen el mismo largo, dilo y deja que Python lo verifique:
names = ['ada', 'grace', 'alan']
scores = [90, 85]
try:
print(list(zip(names, scores, strict=True)))
except ValueError as err:
print('ValueError:', err)
Imprime:
ValueError: zip() argument 2 is shorter than argument 1
strict=True necesita Python 3.10 o más nuevo. Úsalo siempre que los largos iguales sean una suposición y no una coincidencia.
Dos cosas que vale la pena saber
Un zip de claves y valores es un diccionario:
cols = ['id', 'name', 'role']
row = [17, 'ada', 'engineer']
print(dict(zip(cols, row)))
Imprime:
{'id': 17, 'name': 'ada', 'role': 'engineer'}
Y zip(*pairs) deshace el zip, porque desempacar la lista de pares convierte cada par en un argumento separado:
pairs = [('ada', 90), ('grace', 85), ('alan', 71)]
names, scores = zip(*pairs)
print(names)
print(scores)
print(sum(scores) / len(scores))
Imprime:
('ada', 'grace', 'alan')
(90, 85, 71)
82.0
Te devuelve tuplas, no listas. Eso sorprende una vez.
Los dos juntos
names = ['ada', 'grace', 'alan']
scores = [90, 85, 71]
for n, (name, score) in enumerate(zip(names, scores), start=1):
print(f"{n}. {name:6} {score}")
Imprime:
1. ada 90
2. grace 85
3. alan 71
Los paréntesis alrededor de (name, score) son obligatorios. enumerate te da dos cosas, y la segunda es a su vez un par.
Ninguno de los dos te da una lista
names = ['ada', 'grace', 'alan']
scores = [90, 85, 71]
z = zip(names, scores)
print(list(z))
print(list(z))
Imprime:
[('ada', 90), ('grace', 85), ('alan', 71)]
[]
El segundo list(z) está vacío. No se borró nada y nada falló: zip te devolvió algo que se puede recorrer una sola vez, y ya lo recorriste. enumerate, map, filter y un archivo abierto se comportan igual.
Eso no es una rareza de zip. Así funciona la iteración en todo Python, y es el tema del próximo post.
Qué recordar
-
enumerate(x, start=1)en lugar de un contador hecho a mano, y en lugar derange(len(x)). -
ziprecorta hasta la entrada más corta en silencio. Pasastrict=Truecuando los largos iguales sean una suposición. -
dict(zip(claves, valores))arma un diccionario;zip(*pares)lo desarma, en tuplas. -
Los dos devuelven algo que se recorre una sola vez. Envuélvelo en
list()apenas necesites usarlo dos veces.