Bucle for en Python: guía completa con range, enumerate y zip
- El for de Python no es el for de C
- Qué puedes iterar con for
- range(): repetir N veces con control total
- enumerate(): índice y valor al mismo tiempo
- zip(): dos listas en paralelo
- Iterar diccionarios: keys, values e items
- Bucles anidados
- El else del for: la joya oculta de Python
- Errores clásicos con for
- Preguntas frecuentes
for sobre el universo conocido. Su método era siempre el mismo: observar cada caso particular, uno a uno, para inferir la ley general. El bucle for de Python es, en ese sentido, el instrumento filosófico más aristotélico que existe. No proclama verdades a priori — recorre cada elemento, lo procesa, y construye comprensión desde lo concreto. Si Aristóteles hubiera tenido Python, habría escrito for especie in universo_animal: catalogar(especie) y habría terminado antes.
El for de Python no se parece al for de C o Java. No trabaja con índices por defecto, no tiene condición de parada, no tiene incremento manual. Lo que tiene es algo mejor: itera directamente sobre los elementos de cualquier objeto iterable. Menos código, más intención, menos errores. Una vez que lo interiorizas, el for i = 0; i < n; i++ de otros lenguajes parece un trabalenguas innecesario.
🔁 El bucle for: iterar sobre cualquier secuencia
La sintaxis es for elemento in iterable:. En cada iteración, elemento toma el valor del siguiente ítem de iterable. No hay índice, no hay condición, no hay incremento. Python se encarga de todo.
# Recorrer una lista
frutas = ["manzana", "pera", "naranja", "uva"]
for fruta in frutas:
print(fruta)
# manzana
# pera
# naranja
# uva
# Recorrer un string (carácter a carácter)
for letra in "Python":
print(letra, end=" ")
# P y t h o n
# Recorrer una tupla
coordenadas = [(0, 0), (1, 2), (3, 4)]
for punto in coordenadas:
x, y = punto # desempaquetado
print(f"x={x}, y={y}")
# Recorrer un set (sin garantía de orden)
colores = {"rojo", "verde", "azul"}
for color in colores:
print(color) # orden impredecible
# Recorrer los caracteres de un string buscando vocales
vocales = set("aeiouáéíóú")
texto = "Aristóteles"
encontradas = [c for c in texto.lower() if c in vocales]
print(encontradas) # ['a', 'i', 'ó', 'e', 'e']
for: procesar cada elemento de la secuencia en orden, uno a uno, hasta el último. Fuente: Pexels (licencia libre).📏 range(): repetir N veces con control total
Cuando necesitas repetir algo un número determinado de veces —o generar una secuencia numérica— usas range(). Es perezoso: no crea la lista en memoria, genera cada número cuando el bucle lo necesita.
# range(fin): de 0 a fin-1
for i in range(5):
print(i) # 0 1 2 3 4
# range(inicio, fin): de inicio a fin-1
for i in range(1, 6):
print(i) # 1 2 3 4 5
# range(inicio, fin, paso): con incremento
for i in range(0, 20, 5):
print(i) # 0 5 10 15
# Contar hacia atrás (paso negativo)
for i in range(10, 0, -1):
print(i) # 10 9 8 7 ... 1
# range() no incluye nunca el fin:
list(range(5)) # [0, 1, 2, 3, 4]
list(range(1, 5)) # [1, 2, 3, 4]
# range() es perezoso — no crea la lista en memoria:
# range(1_000_000) ocupa 48 bytes constantes
# list(range(1_000_000)) ocuparía ~8 MB
range() solo trabaja con enteros. Si necesitas iterar con paso decimal, usa numpy.arange(0, 1, 0.1) o una comprensión: [i/10 for i in range(10)].
i porque sí. Técnicamente viene de FORTRAN de los años 50, donde las variables I a N eran enteras por defecto. Desde entonces, generaciones enteras de programadores han perpetuado la costumbre sin preguntarse nada. La próxima vez que escribas for i in range(10), recuerda que estás honrando una convención de 1957. Podrías escribir for patata in range(10) y funcionaría exactamente igual. Nadie lo hace. Nadie sabe por qué.
🔢 enumerate(): índice y valor al mismo tiempo
enumerate() devuelve pares (índice, valor) para cada elemento. Es la forma idiomática de Python cuando necesitas el índice y el valor simultáneamente — evita el patrón range(len(...)) que es más verboso y propenso a errores.
frutas = ["manzana", "pera", "naranja", "uva"]
# ❌ Forma no idiomática (viene de C/Java):
for i in range(len(frutas)):
print(i, frutas[i])
# ✅ Forma idiomática Python:
for i, fruta in enumerate(frutas):
print(i, fruta)
# 0 manzana
# 1 pera
# 2 naranja
# 3 uva
# Empezar desde 1 en lugar de 0:
for i, fruta in enumerate(frutas, start=1):
print(f"{i}. {fruta}")
# 1. manzana
# 2. pera
# 3. naranja
# 4. uva
# Caso real: mostrar errores con número de línea
lineas = ["nombre = 'Ana'", "edad = 'veintiocho'", "activo = True"]
for num, linea in enumerate(lineas, start=1):
if "'" in linea and "=" in linea and not linea.startswith("nombre"):
print(f" Línea {num}: posible error de tipo — {linea}")
# enumerate() funciona con cualquier iterable, no solo listas:
for i, char in enumerate("Python"):
print(f" [{i}] = '{char}'")
# [0]='P' [1]='y' [2]='t' [3]='h' [4]='o' [5]='n'
🤐 zip(): dos listas en paralelo
zip() combina dos (o más) iterables emparejando sus elementos por posición. Se detiene cuando el más corto se agota. Para el caso opuesto —continuar hasta el más largo—, usa itertools.zip_longest().
nombres = ["Ana", "Luis", "Marta", "Pedro"]
notas = [8.5, 6.0, 9.2, 4.8]
for nombre, nota in zip(nombres, notas):
estado = "✅ aprobado" if nota >= 5 else "❌ suspenso"
print(f" {nombre:<10} {nota:.1f} {estado}")
# Ana 8.5 ✅ aprobado
# Luis 6.0 ✅ aprobado
# Marta 9.2 ✅ aprobado
# Pedro 4.8 ❌ suspenso
# zip() con tres listas:
materias = ["Matemáticas", "Historia", "Inglés"]
max_notas = [10, 10, 10]
for n, nota, maximo in zip(nombres, notas, max_notas):
porcentaje = nota / maximo * 100
print(f" {n}: {nota}/{maximo} ({porcentaje:.0f}%)")
# Crear un diccionario con zip:
claves = ["nombre", "edad", "ciudad"]
valores = ["Ana", 28, "Madrid"]
perfil = dict(zip(claves, valores))
print(perfil) # {"nombre": "Ana", "edad": 28, "ciudad": "Madrid"}
# zip_longest: continúa hasta el más largo
from itertools import zip_longest
a = [1, 2, 3]
b = ["x", "y"]
for n, c in zip_longest(a, b, fillvalue="—"):
print(n, c)
# 1 x
# 2 y
# 3 —
# zip "inverso": desempaquetar lista de tuplas
pares = [("Ana", 28), ("Luis", 34), ("Marta", 22)]
nombres_sueltos, edades_sueltas = zip(*pares)
print(nombres_sueltos) # ('Ana', 'Luis', 'Marta')
print(edades_sueltas) # (28, 34, 22)
zip(): toma dos listas separadas y las une elemento a elemento, como si deslizaras una cremallera. Fuente: Pexels (licencia libre).🗂️ Iterar diccionarios: keys, values e items
Los diccionarios tienen tres modos de iteración. Por defecto —si haces for k in d— obtienes las claves. Para valores o pares, llamas explícitamente al método correspondiente.
precios = {"café": 1.50, "tostada": 2.20, "zumo": 2.80, "agua": 1.00}
# Iterar sobre claves (modo por defecto):
for producto in precios:
print(producto) # café, tostada, zumo, agua
# Iterar sobre valores:
total = 0
for precio in precios.values():
total += precio
print(f"Total carta: {total:.2f} €") # 7.50 €
# Iterar sobre pares clave-valor (lo más habitual):
for producto, precio in precios.items():
print(f" {producto:<12} {precio:>5.2f} €")
# Ordenar por valor (precio más bajo primero):
for prod, precio in sorted(precios.items(), key=lambda x: x[1]):
print(f" {prod}: {precio:.2f} €")
# Filtrar con comprensión de dict:
caros = {k: v for k, v in precios.items() if v > 2.0}
print(caros) # {"tostada": 2.20, "zumo": 2.80}
# Iterar dos diccionarios a la vez (Python 3.9+):
ayer = {"manzanas": 10, "peras": 5}
hoy = {"manzanas": 8, "peras": 7, "naranjas": 3}
for fruta, stock in hoy.items():
anterior = ayer.get(fruta, 0)
diferencia = stock - anterior
signo = "▲" if diferencia > 0 else "▼" if diferencia < 0 else "="
print(f" {fruta}: {stock} ({signo}{abs(diferencia):+d})")
🔲 Bucles anidados
Un bucle anidado es un for dentro de otro. Por cada iteración del bucle exterior, el interior se ejecuta completo. Útil para matrices, combinaciones, o estructuras de datos bidimensionales.
# Tabla de multiplicar básica:
for i in range(1, 4):
for j in range(1, 4):
print(f"{i}×{j}={i*j}", end=" ")
print() # salto de línea al final de cada fila
# 1×1=1 1×2=2 1×3=3
# 2×1=2 2×2=4 2×3=6
# 3×1=3 3×2=6 3×3=9
# Recorrer una matriz (lista de listas):
matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for fila in matriz:
for elemento in fila:
print(elemento, end=" ")
print()
# Aplanar una matriz con comprensión (más compacto):
plana = [elem for fila in matriz for elem in fila]
print(plana) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Combinaciones de dos listas:
colores = ["rojo", "azul"]
tallas = ["S", "M", "L"]
for color in colores:
for talla in tallas:
print(f"{color}-{talla}", end=" ")
# rojo-S rojo-M rojo-L azul-S azul-M azul-L
# Con itertools.product (equivalente, más limpio):
from itertools import product
for color, talla in product(colores, tallas):
print(f"{color}-{talla}", end=" ")
itertools.product, matrices NumPy, o una estructura de datos diferente.
for: comprobación de elementos disponibles, ejecución del bloque, retorno a la siguiente iteración, salida por fin natural (→ else) o por break. Infografía: Ciberaula.💎 El else del for: la joya oculta de Python
El for tiene una cláusula else que casi ningún programador que viene de otro lenguaje conoce. El bloque else se ejecuta cuando el bucle termina de forma natural — es decir, sin que se haya ejecutado un break. Si hay un break, el else no corre.
# Patrón clásico: búsqueda limpia sin variable bandera
usuarios = [
{"id": 1, "nombre": "Ana", "activo": True},
{"id": 2, "nombre": "Luis", "activo": False},
{"id": 3, "nombre": "Marta", "activo": True},
]
id_buscado = 5
for usuario in usuarios:
if usuario["id"] == id_buscado:
print(f"Encontrado: {usuario['nombre']}")
break
else:
print(f"No existe ningún usuario con id={id_buscado}.")
# → No existe ningún usuario con id=5.
# Comparación: sin for/else necesitas una bandera booleana:
encontrado = False
for usuario in usuarios:
if usuario["id"] == id_buscado:
encontrado = True
print(f"Encontrado: {usuario['nombre']}")
break
if not encontrado:
print("No encontrado.") # más verboso, misma lógica
# Verificar si una lista tiene algún elemento que cumpla condición:
numeros = [4, 8, 15, 16, 23, 42]
for n in numeros:
if n % 7 == 0:
print(f"Primer múltiplo de 7: {n}")
break
else:
print("Ningún múltiplo de 7 en la lista.")
# → Ningún múltiplo de 7 en la lista.
# Verificación de número primo:
def es_primo(n):
if n < 2:
return False
for divisor in range(2, int(n**0.5) + 1):
if n % divisor == 0:
return False # tiene divisor → no es primo, no llega al else
else:
return True # llegó al final sin divisores → es primo
print(es_primo(17)) # True
print(es_primo(18)) # False
🐛 Errores clásicos con for
1. Modificar la lista que se está iterando
numeros = [1, 2, 3, 4, 5, 6]
# ❌ Comportamiento indefinido: se saltan elementos
for n in numeros:
if n % 2 == 0:
numeros.remove(n) # modifica la lista durante la iteración
print(numeros) # [1, 3, 5, 6] ← el 6 no se eliminó
# ✅ Iterar sobre una copia:
for n in numeros[:]:
if n % 2 == 0:
numeros.remove(n)
# ✅ Mejor: list comprehension
numeros = [n for n in numeros if n % 2 != 0]
2. Usar range(len(...)) cuando no es necesario
frutas = ["manzana", "pera", "naranja"]
# ❌ Verboso y propenso a errores de índice:
for i in range(len(frutas)):
print(frutas[i])
# ✅ Iterar directamente:
for fruta in frutas:
print(fruta)
# ✅ Si necesitas el índice: enumerate()
for i, fruta in enumerate(frutas):
print(i, fruta)
3. Confundir for con forEach de JavaScript
numeros = [1, 2, 3, 4, 5]
# ❌ Intento de modificar elementos "in place" (no funciona):
for n in numeros:
n = n * 2 # modifica la variable local, NO el elemento de la lista
print(numeros) # [1, 2, 3, 4, 5] — sin cambios
# ✅ Para transformar: list comprehension
numeros = [n * 2 for n in numeros]
# ✅ Para modificar in-place: usar índice
for i in range(len(numeros)):
numeros[i] *= 2
4. Olvidar que zip() se detiene con el más corto
nombres = ["Ana", "Luis", "Marta", "Pedro"]
notas = [8.5, 6.0] # solo 2 notas
for nombre, nota in zip(nombres, notas):
print(nombre, nota)
# Ana 8.5
# Luis 6.0
# Marta y Pedro ← silenciosamente ignorados
# ✅ Si esto es un error, comprueba las longitudes antes:
if len(nombres) != len(notas):
raise ValueError(f"Listas de longitud diferente: {len(nombres)} vs {len(notas)}")
range() en sus tres formas, enumerate(), zip() con zip_longest, sorted()/reversed(), y las tres formas de iterar diccionarios. Ficha de referencia: Ciberaula.✅ Resumen y próximos pasos
El for de Python itera directamente sobre los elementos de cualquier secuencia — lista, string, tupla, dict, set, generator. range() genera secuencias numéricas sin crear la lista en memoria. enumerate() da índice y valor simultáneamente. zip() une dos o más listas por posición. Y el else del for se ejecuta solo si no hubo break — patrón limpio para búsquedas sin variable bandera.
La siguiente lección: el bucle while — cómo repetir código mientras se cumpla una condición dinámica, y los patrones while True con break para menús e interacción con el usuario.
❓ Preguntas frecuentes
❓ Preguntas frecuentes sobre Bucle for en Python: guía completa con range, enumerate y zip
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre Bucle for en Python: guía completa con range, enumerate y zip? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!