break, continue y pass en Python: control del flujo de bucles
break es la decisión soberana de parar — algo que sí depende de ti. continue es la decisión de ignorar este momento concreto y seguir adelante sin que te arrastre. Y pass es la aceptación filosófica de que aún no tienes nada que decir en este punto — y eso también es una elección válida. Marco Aurelio habría sido un programador extraordinariamente disciplinado: nunca habría olvidado el i += 1 antes del continue.
En las lecciones anteriores vimos cómo el for itera sobre secuencias y el while repite mientras una condición sea verdadera. Ahora añadimos tres sentencias que modifican ese flujo desde dentro del bucle: break para parar en seco, continue para saltar una iteración sin salir, y pass para marcar un bloque vacío que implementarás después.
🎛️ Las tres sentencias de control
Una vista rápida antes de entrar en detalle:
for n in [1, -2, 3, -4, 5]:
if n < 0:
break # para el bucle al primer negativo → imprime solo 1
print(n)
for n in [1, -2, 3, -4, 5]:
if n < 0:
continue # salta los negativos → imprime 1, 3, 5
print(n)
for n in [1, -2, 3, -4, 5]:
if n < 0:
pass # no hace nada → imprime todos: 1, -2, 3, -4, 5
print(n)
break lo termina, continue salta la iteración actual, y pass no modifica nada — solo satisface el requisito sintáctico de que el bloque no esté vacío. Infografía: Ciberaula.🔴 break: salida inmediata del bucle
break termina el bucle en el que se encuentra — sin completar el resto del bloque, sin ejecutar el else, sin iterar más. La ejecución continúa en la primera línea de código después del bucle.
# Buscar el primer elemento que cumple una condición:
precios = [12.50, 8.00, 24.99, 5.50, 31.00]
for precio in precios:
if precio > 20:
print(f"Primer precio mayor de 20€: {precio}")
break
# → Primer precio mayor de 20€: 24.99
# (no sigue buscando después de encontrar el primero)
# break en while — lectura hasta señal de fin:
lineas_leidas = []
while True:
linea = input("Escribe una línea (o 'FIN' para terminar): ")
if linea.strip().upper() == "FIN":
break
lineas_leidas.append(linea)
print(f"Recibidas {len(lineas_leidas)} líneas.")
# break con for/else — el else NO corre si hubo break:
usuarios = [
{"nombre": "Ana", "activo": True},
{"nombre": "Luis", "activo": False},
{"nombre": "Marta", "activo": True},
]
for u in usuarios:
if not u["activo"]:
print(f"Usuario inactivo encontrado: {u['nombre']}")
break
else:
print("Todos los usuarios están activos.")
# → Usuario inactivo encontrado: Luis
# break sale solo del bucle inmediato (no de los anidados externos):
for i in range(3):
for j in range(3):
if j == 1:
break # solo sale del for j
print(f" i={i} j={j}")
print(f"Fila {i} completa")
# i=0 j=0 | Fila 0 completa | i=1 j=0 | Fila 1 completa | ...
break hace exactamente lo mismo con el bucle: interrumpe el flujo de forma inmediata e irreversible, sin terminar lo que quedaba por hacer. Fuente: Pexels (licencia libre).🔵 continue: saltar al siguiente elemento
continue interrumpe la iteración actual y vuelve al inicio del bucle. En un for, avanza al siguiente elemento. En un while, reevalúa la condición. El bucle no termina — simplemente se salta el resto del bloque en esa iteración.
# Procesar solo los válidos, ignorar el resto:
datos = [42, None, 7, "", 15, None, 3]
suma = 0
for dato in datos:
if dato is None or dato == "":
continue # salta este elemento
suma += dato
print(f"Suma de válidos: {suma}") # 67
# Versión equivalente sin continue (a veces más legible):
suma = sum(d for d in datos if d is not None and d != "")
# continue en while — pedir hasta tener suficientes válidos:
validos = []
while len(validos) < 5:
entrada = input("Número positivo: ").strip()
try:
n = float(entrada)
except ValueError:
print("Eso no es un número.")
continue # vuelve al while sin contar este intento
if n <= 0:
print("Debe ser mayor que cero.")
continue
validos.append(n) # solo llega aquí si es válido
print(f"Media: {sum(validos)/len(validos):.2f}")
# Múltiples condiciones de descarte (más legible con continue):
registros = [
{"id": 1, "activo": True, "saldo": 150},
{"id": 2, "activo": False, "saldo": 80},
{"id": 3, "activo": True, "saldo": -10},
{"id": 4, "activo": True, "saldo": 200},
]
for reg in registros:
if not reg["activo"]:
continue # ignorar inactivos
if reg["saldo"] < 0:
continue # ignorar con deuda
enviar_oferta(reg["id"]) # solo activos con saldo positivo
continue es la sentencia de Python más utilizada por personas que aprendieron a programar en Java y todavía no se han reconciliado con las list comprehensions. No es que esté mal — es completamente válido y a veces más legible que la alternativa. Es solo que existe una correlación estadística sospechosa entre "lleva años usando Java" y "escribe for x in lista: if not condicion: continue; procesar(x)" en lugar de "[procesar(x) for x in lista if condicion]". Los científicos no han estudiado esto. Probablemente deberían.
🟢 pass: el marcador de posición silencioso
pass es la sentencia nula de Python: no hace absolutamente nada. Su única función es satisfacer el requisito sintáctico de que cualquier bloque abierto con dos puntos (:) contenga al menos una sentencia. Sin pass, un bloque vacío produce SyntaxError.
# Sin pass → SyntaxError:
# def funcion():
# ← IndentationError / SyntaxError
# Con pass → código válido que no hace nada:
def funcion_pendiente():
pass # TODO: implementar en la próxima sesión
class ModeloML:
pass # placeholder hasta definir atributos y métodos
# Clase de excepción personalizada (patrón muy común):
class ErrorConexionTimeout(Exception):
pass
class ErrorAutenticacion(ValueError):
pass
# Probar: se pueden lanzar normalmente
try:
raise ErrorConexionTimeout("Servidor no responde")
except ErrorConexionTimeout as e:
print(f"Error capturado: {e}")
# pass en if/elif cuando una rama no requiere acción:
for evento in cola_eventos:
if evento.tipo == "CLICK":
manejar_click(evento)
elif evento.tipo == "TECLA":
manejar_tecla(evento)
elif evento.tipo == "MOUSE_MOVE":
pass # eventos de movimiento ignorados por ahora
else:
log.warning(f"Evento desconocido: {evento.tipo}")
# pass como cuerpo de bucle de espera (poco frecuente):
import time
inicio = time.time()
while not recurso_disponible() and time.time() - inicio < 5.0:
pass # espera activa — considera time.sleep(0.1) para no saturar la CPU
pass hace exactamente eso: define el esqueleto del código (la función, la clase, el bloque) sin poner nada dentro todavía. La obra continuará más tarde. Fuente: Pexels (licencia libre)..pyi), es común ver ... en lugar de pass como cuerpo de función. Ambos son válidos sintácticamente. La convención es usar pass en código de aplicación y ... en type stubs o cuando el código se usa en contextos de tipado estático. En un día a día normal: usa pass.
🔲 Salir de bucles anidados
El break sale solo del bucle inmediato. En bucles anidados, salir de todos los niveles a la vez requiere una estrategia explícita. Hay tres opciones según la situación:
matriz = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
]
objetivo = 7
# ── Opción 1: variable bandera (verbosa pero siempre funciona) ──
encontrado = False
fila_enc = col_enc = -1
for i, fila in enumerate(matriz):
for j, valor in enumerate(fila):
if valor == objetivo:
fila_enc, col_enc = i, j
encontrado = True
break
if encontrado:
break
if encontrado:
print(f"Encontrado en [{fila_enc}][{col_enc}]") # [1][2]
# ── Opción 2: función con return (la más limpia) ──
def buscar_en_matriz(m, obj):
for i, fila in enumerate(m):
for j, val in enumerate(fila):
if val == obj:
return i, j # sale de todos los bucles Y de la función
return None
pos = buscar_en_matriz(matriz, objetivo)
if pos:
print(f"Encontrado en {pos}") # (1, 2)
# ── Opción 3: excepción como señal de salida (poco habitual) ──
class Encontrado(Exception):
def __init__(self, i, j):
self.pos = (i, j)
try:
for i, fila in enumerate(matriz):
for j, val in enumerate(fila):
if val == objetivo:
raise Encontrado(i, j)
except Encontrado as e:
print(f"Encontrado en {e.pos}")
# ── Opción 4: itertools.product (evita el anidamiento) ──
from itertools import product
for (i, j) in product(range(len(matriz)), range(len(matriz[0]))):
if matriz[i][j] == objetivo:
print(f"Encontrado en [{i}][{j}]")
break
🔧 Patrones prácticos combinados
# ── Patrón: leer un archivo línea a línea hasta encontrar un marcador ──
def leer_hasta_marcador(ruta, marcador="---FIN---"):
"""Lee líneas de un archivo hasta encontrar el marcador o EOF."""
lineas = []
with open(ruta, "r", encoding="utf-8") as f:
for linea in f:
linea = linea.rstrip("\n")
if linea == marcador:
break
if not linea or linea.startswith("#"):
continue # ignorar líneas vacías y comentarios
lineas.append(linea)
return lineas
# ── Patrón: reintentar con límite y espera exponencial ──
import time
def ejecutar_con_reintentos(funcion, max_intentos=5, espera_base=1.0):
for intento in range(1, max_intentos + 1):
try:
return funcion()
except (ConnectionError, TimeoutError) as e:
if intento == max_intentos:
raise # reraise en el último intento
espera = espera_base * (2 ** (intento - 1))
print(f"Intento {intento} fallido. Reintentando en {espera:.1f}s…")
time.sleep(espera)
# nunca llega aquí (el raise del except lo impide)
# ── Patrón: procesamiento por lotes con break de seguridad ──
LIMITE_SEGURIDAD = 10_000
def procesar_cola(cola):
procesados = 0
errores = []
while cola and procesados < LIMITE_SEGURIDAD:
item = cola.pop(0)
try:
procesar_item(item)
procesados += 1
except ValueError as e:
errores.append((item, str(e)))
continue # no cuenta como procesado, pero sigue
except Exception:
break # error grave: parar todo
if procesados >= LIMITE_SEGURIDAD:
print(f"⚠️ Límite de seguridad alcanzado ({LIMITE_SEGURIDAD} items)")
return procesados, errores
🐛 Errores comunes
1. Esperar que break salga de todos los bucles anidados
# ❌ Error conceptual: break solo sale del for j
for i in range(3):
for j in range(3):
if i == 1 and j == 1:
break # solo sale del for j; el for i continúa
# ✅ Encapsula en función si necesitas salir de todo:
def buscar(m, obj):
for i, fila in enumerate(m):
for j, v in enumerate(fila):
if v == obj:
return (i, j)
return None
2. continue antes de actualizar en while
# ❌ Bucle infinito: i nunca se incrementa cuando i==2
i = 0
while i < 5:
if i == 2:
continue # i queda en 2 para siempre
i += 1
# ✅ Actualiza ANTES del continue:
i = 0
while i < 5:
i += 1 # siempre se ejecuta
if i == 3:
continue
print(i) # 1 2 4 5
3. Confundir pass con continue o con no-op
# pass NO salta la iteración — solo ocupa el bloque vacío:
for n in [1, 2, 3, 4, 5]:
if n == 3:
pass # no hace nada; print(n) SÍ se ejecuta
print(n) # imprime 1 2 3 4 5 (todos, incluido 3)
# Si quieres saltarte el 3, usa continue:
for n in [1, 2, 3, 4, 5]:
if n == 3:
continue # salta el print(n)
print(n) # imprime 1 2 4 5
break en bucles anidados, la trampa del continue en while con actualización, y los cinco usos principales de pass. Ficha de referencia: Ciberaula.✅ Resumen y próximos pasos
break termina el bucle inmediato (sin ejecutar el else). continue salta al siguiente elemento o iteración. pass no hace nada — es un marcador sintáctico para bloques vacíos. Para salir de bucles anidados la solución más limpia es encapsular en una función y usar return.
Con esto cerramos el Módulo 2 de control de flujo. La siguiente lección: comprensiones de listas — la forma más Pythónica de transformar, filtrar y construir listas en una sola expresión, con una sintaxis que reemplaza elegantemente muchos bucles for con append.
❓ Preguntas frecuentes
❓ Preguntas frecuentes sobre break, continue y pass en Python: control del flujo de bucles
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre break, continue y pass en Python: control del flujo de bucles? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!