Los bucles permiten repetir instrucciones sin copiar el mismo código muchas veces. Python ofrece dos estructuras principales: for, pensado para recorrer elementos de un iterable, y while, pensado para repetir mientras una condición sea verdadera. Elegir correctamente entre ambos hace que el programa sea más claro, seguro y fácil de mantener.
Para profundizar, consulta las guías sobre bucles for, bucles while, la función range(), enumerate() y zip(). Como referencias externas, revisa el tutorial oficial de control de flujo y la referencia oficial de sentencias compuestas.
Qué problema resuelve un bucle
Sin un bucle, imprimir cinco números exigiría repetir instrucciones:
print(1)
print(2)
print(3)
print(4)
print(5)
Con for, la intención es más clara:
for numero in range(1, 6):
print(numero)
El programa expresa que quiere recorrer una secuencia de números, no que desea ejecutar cinco líneas independientes.
El bucle for
for obtiene un elemento del iterable en cada iteración:
frutas = ["manzana", "pera", "naranja"]
for fruta in frutas:
print(fruta)
No necesitas controlar manualmente un índice. La variable fruta recibe cada valor en orden.
Qué es un iterable
Un iterable es un objeto que puede recorrerse. Strings, listas, tuplas, sets, diccionarios, rangos, archivos y generadores son ejemplos comunes.
for letra in "Python":
print(letra)
El bucle recorre los caracteres. En un set no debes depender del orden. En un diccionario, el recorrido directo produce las claves.
Recorrer diccionarios
usuario = {
"nombre": "Ana",
"edad": 30,
"activo": True,
}
for clave, valor in usuario.items():
print(f"{clave}: {valor}")
items() produce pares de clave y valor. keys() devuelve claves y values(), valores.
Usar range()
for numero in range(5):
print(numero)
El resultado va de cero a cuatro. El límite final no se incluye. También puedes indicar inicio y paso:
for numero in range(2, 11, 2):
print(numero)
Este ejemplo imprime pares desde dos hasta diez.
Conteos regresivos
for numero in range(5, 0, -1):
print(numero)
print("¡Comenzamos!")
El paso negativo hace que el rango disminuya. El límite cero queda excluido.
Índices con enumerate()
tareas = ["estudiar", "practicar", "probar"]
for indice, tarea in enumerate(tareas, start=1):
print(f"{indice}. {tarea}")
enumerate() evita crear un contador manual y reduce errores de sincronización.
Combinar colecciones con zip()
nombres = ["Ana", "Luis", "Marta"]
notas = [8, 7, 9]
for nombre, nota in zip(nombres, notas, strict=True):
print(f"{nombre}: {nota}")
strict=True detecta longitudes diferentes en versiones modernas de Python. Sin esa opción, zip termina con la colección más corta.
El bucle while
while repite mientras su condición sea verdadera:
contador = 1
while contador <= 5:
print(contador)
contador += 1
Debes asegurarte de que la condición pueda cambiar. Si el contador nunca aumenta, el bucle será infinito.
Validar con while
respuesta = ""
while respuesta not in {"s", "n"}:
respuesta = input("¿Continuar? (s/n): ").strip().lower()
print(f"Respuesta válida: {respuesta}")
No sabemos cuántos intentos necesitará el usuario, por lo que while es natural.
for frente a while
Usa for cuando recorres una colección, un archivo, un rango o un generador. Usa while cuando la repetición depende de un estado que cambia: vidas restantes, conexión disponible, respuesta válida o cola no vacía.
# Claro con for
for archivo in archivos:
procesar(archivo)
# Claro con while
while cola:
procesar(cola.popleft())
Ambos pueden resolver muchos de los mismos problemas, pero la mejor opción expresa el motivo de la repetición.
break
break termina el bucle más cercano:
numeros = [4, 8, 15, 16, 23, 42]
objetivo = 15
for numero in numeros:
if numero == objetivo:
print("Encontrado")
break
No uses break para ocultar una condición de salida confusa. Debe resultar evidente por qué termina el ciclo.
continue
for numero in range(1, 11):
if numero % 2 != 0:
continue
print(numero)
continue salta el resto de la iteración actual. Es útil para descartar valores pronto y evitar bloques muy anidados.
else en bucles
for usuario in usuarios:
if usuario["email"] == email_buscado:
encontrado = usuario
break
else:
encontrado = None
El else se ejecuta cuando el bucle termina normalmente, sin break. También funciona con while. Es útil en búsquedas, pero no siempre resulta familiar; úsalo cuando mejore la claridad.
Bucles anidados
for fila in range(3):
for columna in range(3):
print(f"({fila}, {columna})")
El bucle interior se ejecuta completo por cada vuelta exterior. Tres por tres produce nueve combinaciones. La cantidad de trabajo puede crecer rápidamente con datos grandes.
Recorrer una matriz
matriz = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
for fila in matriz:
for valor in fila:
print(valor, end=" ")
print()
Para operaciones numéricas grandes, bibliotecas como NumPy suelen ofrecer alternativas vectorizadas más eficientes.
Comprensiones
cuadrados = [numero ** 2 for numero in range(10)]
pares = [numero for numero in range(20) if numero % 2 == 0]
Las comprensiones son apropiadas para transformaciones y filtros simples. Si incluyen varias condiciones, efectos secundarios o lógica difícil, un bucle normal es más legible.
Comprensiones de diccionario y set
cuadrados_por_numero = {
numero: numero ** 2
for numero in range(5)
}
longitudes = {
len(palabra)
for palabra in ["sol", "casa", "python"]
}
El set elimina longitudes duplicadas. El diccionario relaciona cada número con su cuadrado.
Generadores
cuadrados = (numero ** 2 for numero in range(1_000_000))
for cuadrado in cuadrados:
procesar(cuadrado)
Una expresión generadora produce valores bajo demanda, lo que puede reducir el consumo de memoria. Solo puede recorrerse una vez salvo que se cree nuevamente.
Recorrer archivos
from pathlib import Path
with Path("datos.txt").open("r", encoding="utf-8") as archivo:
for numero_linea, linea in enumerate(archivo, start=1):
contenido = linea.strip()
if contenido:
print(numero_linea, contenido)
El archivo se procesa línea por línea sin cargarlo completamente en memoria.
Modificar una colección durante el recorrido
Eliminar elementos de la misma lista que recorres puede saltar valores:
# Evitar
for numero in numeros:
if numero < 0:
numeros.remove(numero)
Crea una nueva lista o recorre una copia:
numeros = [numero for numero in numeros if numero >= 0]
Una cola con while
from collections import deque
cola = deque(["pedido-1", "pedido-2", "pedido-3"])
while cola:
pedido = cola.popleft()
procesar(pedido)
La condición aprovecha que una colección vacía se considera falsa.
Reintentos limitados
intento = 0
max_intentos = 3
while intento < max_intentos:
intento += 1
try:
conectar()
except ConnectionError:
print(f"Fallo {intento}/{max_intentos}")
else:
print("Conectado")
break
else:
print("No fue posible conectar")
Todo reintento debe tener un límite, una estrategia de espera y manejo claro del fallo definitivo.
Evitar bucles infinitos
# Incorrecto
activo = True
while activo:
print("Procesando")
Si ninguna instrucción cambia activo o ejecuta break, el proceso no termina. Antes de escribir un while, identifica explícitamente la condición de salida.
Rendimiento
No optimices solo sustituyendo un tipo de bucle por otro. Primero revisa el algoritmo y la estructura de datos. Buscar repetidamente en una lista puede ser más costoso que usar un set. Procesar datos uno por uno en Python puede ser menos eficiente que usar operaciones vectorizadas de una biblioteca especializada.
Efectos secundarios
Un bucle que modifica archivos, envía correos o realiza solicitudes debe manejar fallos parciales. Registra qué elementos fueron completados, evita duplicaciones y permite reanudar el proceso cuando sea necesario.
Funciones para reducir complejidad
def es_valido(usuario):
return usuario.get("activo") and usuario.get("email")
for usuario in usuarios:
if not es_valido(usuario):
continue
enviar_notificacion(usuario)
Extraer una condición con un nombre claro hace que el bucle describa el flujo principal.
Pruebas
Prueba colecciones vacías, un único elemento, varios elementos, salida temprana y valores inválidos. Para un while, comprueba que existe un límite y que la condición inicial falsa no ejecuta el cuerpo.
def sumar_hasta(limite):
total = 0
for numero in range(limite + 1):
total += numero
return total
assert sumar_hasta(0) == 0
assert sumar_hasta(3) == 6
Errores frecuentes
Los fallos habituales son usar índices manuales cuando enumerate sería más claro, olvidar actualizar una condición de while, modificar una colección durante el recorrido, crear demasiados niveles anidados y asumir que zip avisa sobre longitudes diferentes sin strict=True.
Proyectos para practicar
Crea una tabla de multiplicar con for, un menú con while, un buscador que use break, un procesador de archivos con enumerate, un juego con vidas y una comparación de listas con zip. Después añade funciones y tests.
Conclusión
for recorre iterables; while repite según una condición. Utiliza range, enumerate y zip para evitar contadores y sincronizaciones manuales. Emplea break, continue y else solo cuando hagan el flujo más claro. Una buena elección de bucle debe mostrar qué se recorre, qué condición controla la repetición y cómo termina el proceso.






