PyDA Course
📖 Lección 10 Principiante ⏱ 12 minutos ⚡ +10 XP ColabKagglenbviewerBinderDeepnoteGitHub range · enumerate · zip · iteración

Range, Enumerate y Zip

Genera secuencias de números, rastrea índices y combina iterables.

🎯 Lo que aprenderás:
  • Usar range() para generar secuencias de números
  • Usar enumerate() para obtener índice + valor durante la iteración
  • Usar zip() para iterar sobre varias secuencias en paralelo
  • Escribir bucles pitónicos que eviten el rastreo manual de índices

Tres herramientas que superan al conteo manual

Los bucles te dieron la repetición; esta lección te entrega los tres ayudantes que te quitan el conteo de las manos. Cada uno reemplaza un hábito que te enseñaron a escribir a mano, y cada uno es la respuesta a una irritación recurrente: generar números, necesitar la posición de un elemento y emparejar dos listas. Juntos son la diferencia entre un bucle que teclea y un bucle que se lee.

Range: la secuencia aritmética, perezosa

En la lección anterior sumaste con range(5). Merece una mirada de cerca, es la herramienta clásica para “hacer esto un número conocido de veces”:

python
for i in range(5):
    print(i)  # 0 1 2 3 4

range tiene tres formas, espejo de la progresión aritmética a,a+d,a+2d,a, a+d, a+2d, \ldots:

python
range(5)        # 0, 1, 2, 3, 4
range(2, 8)     # 2, 3, 4, 5, 6, 7
range(0, 20, 3) # 0, 3, 6, 9, 12, 15, 18

Un argumento da 0,1,,n10, 1, \ldots, n-1; dos dan el intervalo semiabierto [start,stop)[\text{start}, \text{stop}); tres añaden la diferencia común dd. Por lo decisivo, range es perezoso: registra los parámetros y calcula cada valor solo cuando el bucle lo pide. Pedir un millón de pasos cuesta en memoria lo mismo que pedir cinco, la secuencia jamás se materializa.

Enumerate: la posición, sin el contador

¿Quieres la posición de cada elemento? El instinto de novato es un contador manual:

python
fruits = ["apple", "banana", "cherry"]

i = 0
for fruit in fruits:
    print(f"{i}: {fruit}")
    i += 1

El i += 1 es una tentación a desincronizarse: olvida uno y las etiquetas de posición se desordenan. enumerate produce ambas mitades en un paso, el índice y el elemento, así que no hay nada que mantener al día:

python
for i, fruit in enumerate(fruits):
    print(f"{i}: {fruit}")

# Los encuestadores numeran a la gente desde 1:
for i, fruit in enumerate(fruits, start=1):
    print(f"{i}: {fruit}")

Donde un matemático escribe bi=ai+ib_i = a_i + i para pegar la posición al valor, enumerate entrega el par (i,ai)(i, a_i) directo al cuerpo del bucle.

Zip: alineación por posición

Dos listas paralelas, nombres y notas, suplican leerse juntas. zip las alinea elemento por elemento:

python
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

for name, score in zip(names, scores):
    print(f"{name}: {score}")
# Alice: 85
# Bob: 92
# Charlie: 78

El emparejamiento es el truco cartesiano de correr por ambas listas con un solo cursor, formando las tuplas (n0,s0),(n1,s1),(n_0, s_0), (n_1, s_1), \ldots. Cuando las listas difieren en longitud, el emparejamiento se detiene en la más corta, así que nada queda a medio emparejar. Si también necesitas la cola torcida, itertools.zip_longest la rellena:

python
import itertools
for pair in itertools.zip_longest([1, 2], [3, 4, 5], fillvalue=0):
    print(pair)  # (1, 3), (2, 4), (0, 5) — ninguna valor se pierde

Un ejemplo resuelto: el registro de la clase

Observa cómo se componen las tres herramientas. Una profesora tiene una lista de nombres y una lista paralela de notas, y quiere un informe numerado:

python
names = ["Dina", "Omar", "Sara"]
scores = [78, 91, 85]

for i, (name, score) in enumerate(zip(names, scores), start=1):
    print(f"#{i} {name}: {score}")
# #1 Dina: 78
# #2 Omar: 91
# #3 Sara: 85

print(f"Top score: {max(scores)}")   # Top score: 91

Lee el encabezado del bucle de dentro hacia afuera: zip empareja cada nombre con su nota; los paréntesis (name, score) desempaquetan ese par; enumerate numera los pares empezando en uno. Cuatro gestos que te habrían costado un contador escrito a mano ahora se leen como la frase que describen, la posición se une al valor, par por par, exactamente como bi=ai+ib_i = a_i + i une un índice a cada término.

Errores comunes

  • range es excluyente arriba. range(5) produce 0,1,2,3,40, 1, 2, 3, 4, cinco números, ninguno igual a 55. Piensa en intervalo semiabierto, [0,5)[0, 5).
  • enumerate sobre un dict. Iterar un dict da sus claves; enumerate numeraría las claves, no los pares. Usa dict.items() cuando quieras clave y valor.
  • zip con longitudes desiguales. Los elementos más allá de la entrada corta se desvanecen en silencio. Nota la pérdida, o rellena con zip_longest.
  • zip es un iterador de un solo uso. En Python 3, p = zip(a, b) te da un iterador, no una lista: list(p) lo consume, y un segundo list(p) queda vacío. Convierte con prisas con list(zip(a, b)) cuando vayas a volver a visitar los pares.

🧩 Desafíos

🧩 Desafío, piensa primero, luego revela

Usa enumerate para imprimir cada color de colors = ["red", "green", "blue"] con su posición empezando en 1.

💡 Respuesta: for i, color in enumerate(colors, 1): print(f"{i}. {color}"), el argumento start renumera los pares desde uno.

🧩 Desafío, piensa primero, luego revela

Con keys = ["a", "b"] y values = [1, 2], usa zip para construir un diccionario.

💡 Respuesta: dict(zip(keys, values)){"a": 1, "b": 2}, los pares alineados se vuelven las entradas del mapeo.

🤔 Preguntas socráticas

  • ¿Por qué preferir range a deletrear la lista [0, 1, 2, 3, 4]? ¿Qué cambia cuando la lista tendría un millón de números?
  • Puesto que zip se detiene en la entrada más corta, ¿cómo detectarías qué lado fue el corto? ¿Cuándo importa esa distinción?
  • ¿Puede enumerate pasearse por un dict? ¿Qué numeran exactamente los índices?

✅ Comprobación rápida

1. ¿Qué es list(range(1, 10, 2))?

2. ¿Qué devuelve list(zip([1, 2], [3, 4, 5]))?

rangeenumeratezipiteración