break, continue y pass en Python: control del flujo de bucles

📅 Actualizado en marzo 2026 📊 Nivel: Principiante ⏱️ 14 min de lectura
Ἐφ' ἡμῖν ἐστι, καὶ οὐκ ἐφ' ἡμῖν
«Hay cosas que dependen de nosotros, y cosas que no dependen»
Marco Aurelio · Emperador filósofo romano · 121 d.C. – 180 d.C.
Marco Aurelio fue emperador de Roma durante casi veinte años mientras escribía en privado las Meditaciones — un diario filosófico nunca pensado para ser publicado. La idea central del estoicismo que practicaba era precisamente la de esta lección: distinguir lo que está bajo tu control de lo que no lo está, y actuar en consecuencia. 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)
Comparativa de break, continue y pass en Python: tres columnas con diagramas de flujo individuales, descripción y ejemplos de código para cada sentencia
Las tres sentencias modifican el flujo del bucle de formas completamente distintas: 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 | ...
Mano accionando un disyuntor en un cuadro eléctrico lleno de cables de colores amarillo, verde, azul y negro
Un disyuntor corta el circuito al instante cuando se activa. 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
🎲 Dato inútil pero verdadero: 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
Vista aérea con efecto tilt-shift de una obra de construcción con dos obreros sobre una losa de hormigón, con andamios y estructuras metálicas alrededor
La estructura existe — los cimientos, las paredes exteriores — pero el interior está vacío y en construcción. 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).
💡 pass vs ... (Ellipsis): En Python moderno, especialmente en archivos de tipos (.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
Ficha de referencia de break, continue y pass en Python con patrones avanzados de bucles anidados, continue en while, y todos los usos de pass incluyendo clases abstractas y excepciones
Referencia rápida: patrones avanzados de 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.

break termina el bucle completamente: la ejecución salta al código que hay después del bucle. continue interrumpe solo la iteración actual: la ejecución vuelve al inicio del bucle para evaluar la siguiente iteración. Con break ya no hay más iteraciones; con continue el bucle sigue, pero la iteración actual queda incompleta.
Solo del bucle más cercano que lo contiene. Si tienes un for dentro de otro for y ejecutas break en el interior, solo sales del for interior; el for exterior continúa su siguiente iteración. Para salir de todos los niveles de anidamiento a la vez, las opciones más limpias son: envolver los bucles en una función y usar return, o usar una variable bandera booleana que se comprueba en cada nivel.
Python exige al menos una sentencia en cualquier bloque que abres con dos puntos. Si abres un if, for, while, def o class y no tienes aún nada que poner, necesitas pass obligatoriamente. Sin él obtienes un SyntaxError. pass es tu forma de decirle a Python que el bloque está vacío a propósito, no por error.
continue no es malo, pero en muchos casos una list comprehension o una condición de filtro invertida es más legible. En vez de for x in lista: if x < 0: continue; procesar(x) puedes escribir for x in lista: if x >= 0: procesar(x), que es más directo. Las comprehensions van un paso más allá: [procesar(x) for x in lista if x >= 0]. Úsalo cuando la lógica de salto es compleja o cuando hay múltiples condiciones de descarte.
Sí. break y continue dentro de un bloque try o except funcionan exactamente igual que en cualquier otro contexto dentro del bucle. El bloque finally siempre se ejecuta antes de que el break o continue surtan efecto, lo que permite liberar recursos limpiamente incluso al salir del bucle.
Ambos son válidos como cuerpo de un bloque vacío, pero tienen semánticas distintas. pass es la sentencia nula explícita — no hace nada, no produce ningún valor. ... (el objeto Ellipsis) es un objeto de Python que evalúa a truthy y tiene un valor; usarlo como cuerpo de función es válido sintácticamente y se ha popularizado como alternativa visual a pass, especialmente en type stubs y código de tipado estático. En la práctica, pass es más claro para cuerpos vacíos en código de aplicación; ... es más habitual en archivos .pyi de stubs de tipos.
Valora este artículo

💬 Foro de discusión

¿Tienes dudas sobre break, continue y pass en Python: control del flujo de bucles? Comparte tu pregunta con la comunidad.

¿Tienes cuenta? o comenta como invitado ↓

Todavía no hay mensajes. ¡Sé el primero en participar!