Ya sabes decidir con if y repetir con for y while, pero tus bucles todavía son rígidos: recorren la secuencia completa sí o sí. En esta lección aprenderás a tomar el control fino de la ejecución: break para salir de un bucle en cuanto encuentres lo que buscas, continue para saltarte elementos, pass como marcador de posición, la poco conocida cláusula else de los bucles, el patrón while True para menús que se repiten hasta que el usuario decide salir, y la moderna sentencia match/case de Python 3.10. Con todas ellas construiremos el primer menú interactivo de Papyrus, el esqueleto sobre el que crecerá la aplicación de Ana durante el resto del curso.
Contenido
break: salir del bucle antes de tiempocontinue: saltar a la siguiente iteraciónpass: la instrucción que no hace nada- La cláusula
elseen bucles - Bucles infinitos controlados:
while True+break match/case: selección por patrones (Python 3.10+)- Proyecto: el menú interactivo de Papyrus
break: salir del bucle antes de tiempo
La sentencia break ("romper") termina el bucle inmediatamente: Python salta a la primera instrucción después del bucle, sin completar la vuelta actual ni hacer las restantes.
El caso de uso por excelencia es la búsqueda: Luis pregunta si Papyrus tiene Hamlet. En cuanto lo encontramos, no tiene sentido seguir mirando el resto del catálogo.
catalogo = ["La Odisea", "Hamlet", "El Quijote"]
buscado = "Hamlet"
for titulo in catalogo:
print(f"Comprobando '{titulo}'...")
if titulo == buscado:
print(f"¡Encontrado! '{buscado}' está en el catálogo.")
break
print("Búsqueda terminada.")Salida:
Comprobando 'La Odisea'... Comprobando 'Hamlet'... ¡Encontrado! 'Hamlet' está en el catálogo. Búsqueda terminada.
Fíjate: El Quijote nunca se comprueba. Con un catálogo de 3 libros da igual, pero con 30 000 la diferencia es enorme.
Detalles importantes de break:
- Solo se puede usar dentro de un bucle (
forowhile); fuera produceSyntaxError. - En bucles anidados,
breaksale solo del bucle más interno que lo contiene, no de todos. - Casi siempre aparece dentro de un
if: "si pasa esto, deja de iterar".
continue: saltar a la siguiente iteración
Mientras break abandona el bucle, continue ("continuar") abandona solo la vuelta actual: salta directamente a la siguiente iteración, sin ejecutar el resto del bloque.
Ana quiere imprimir la lista de libros vendibles, saltándose los agotados:
catalogo = ["La Odisea", "Hamlet", "El Quijote", "Fausto"]
stocks = [4, 6, 8, 0]
print("Libros disponibles para la venta:")
for i, titulo in enumerate(catalogo):
if stocks[i] == 0:
continue # agotado: pasamos al siguiente sin imprimir nada
print(f"- {titulo} ({stocks[i]} uds.)")Salida:
Fausto (recién llegado al catálogo, pero con stock 0) se salta limpiamente. El patrón if condición_de_descarte: continue al principio del bloque se llama a veces "cláusula de guarda" y tiene una gran ventaja de legibilidad: el caso "normal" del bucle queda sin indentación extra, en lugar de envolverlo todo en un if gigante.
Comparativa rápida de las dos sentencias:
| Sentencia | Qué interrumpe | El bucle... | Uso típico |
|---|---|---|---|
break |
El bucle entero | Termina ya | "Encontrado, dejo de buscar" |
continue |
Solo la vuelta actual | Sigue con la siguiente | "Este elemento no me interesa, siguiente" |
flowchart TD
A[Siguiente elemento] --> B{¿descartar?}
B -- "continue" --> A
B -- No --> C[Procesar elemento]
C --> D{¿objetivo cumplido?}
D -- "break" --> E[Salir del bucle]
D -- No --> A
pass: la instrucción que no hace nada
pass es exactamente eso: una sentencia vacía. Existe porque la sintaxis de Python exige que todo bloque indentado contenga al menos una instrucción; cuando aún no sabes qué poner (o deliberadamente no quieres hacer nada), pass rellena el hueco sin efectos.
opcion = "3"
if opcion == "1":
print("Mostrando catálogo...")
elif opcion == "2":
pass # TODO: búsqueda de libros, la implementaremos más abajo
else:
print("Opción no reconocida.")Sin el pass, ese elif con el cuerpo vacío daría IndentationError. Sus usos habituales:
- Esqueletos: escribir primero la estructura del programa (ramas, bucles, y en el futuro funciones y clases) y rellenar los bloques poco a poco.
- Ignorar casos a propósito, dejando constancia explícita de que no es un olvido.
No confundas pass con continue: dentro de un bucle, pass no salta nada (la ejecución sigue en la línea siguiente); simplemente "no hace nada" y continúa el flujo normal.
La cláusula else en bucles
Aquí llega una peculiaridad muy de Python: los bucles for y while pueden llevar un bloque else. Su significado sorprende al principio: el else se ejecuta si el bucle terminó de forma natural, sin break.
¿Para qué sirve? Para el patrón de búsqueda que vimos antes: ¿qué pasa si el libro no está? Sin else necesitaríamos una variable "bandera" (flag):
# Versión con bandera (funciona, pero más ruido)
catalogo = ["La Odisea", "Hamlet", "El Quijote"]
buscado = "Fausto"
encontrado = False
for titulo in catalogo:
if titulo == buscado:
encontrado = True
break
if encontrado:
print(f"'{buscado}' está en el catálogo.")
else:
print(f"'{buscado}' no está. Ana puede pedirlo al distribuidor.")Con el else del bucle, la bandera desaparece:
# Versión con for/else (idiomática en Python)
catalogo = ["La Odisea", "Hamlet", "El Quijote"]
buscado = "Fausto"
for titulo in catalogo:
if titulo == buscado:
print(f"'{buscado}' está en el catálogo.")
break
else:
print(f"'{buscado}' no está. Ana puede pedirlo al distribuidor.")Salida:
Cómo leerlo sin confundirse: piensa en el else del bucle como un "si no hubo break" (algunos lo llaman la cláusula nobreak). El flujo exacto:
- El bucle recorre todos los elementos sin que se ejecute
break→ al acabar, se ejecuta elelse. - Se ejecuta un
breaken cualquier vuelta → elelsese salta.
Dos advertencias:
- El
elseva alineado con elfor/while, no con ningúnifinterior. Un despiste de indentación cambia el significado por completo. - Es una construcción poco conocida incluso entre programadores con experiencia; úsala para búsquedas (su caso estrella) y acompáñala de un comentario si crees que quien lea el código puede no conocerla.
También funciona con while: el else se ejecuta cuando la condición se vuelve falsa (salida natural), pero no si sales con break.
Bucles infinitos controlados: while True + break
En la lección anterior el bucle infinito era un error a evitar. Pero hay una versión deliberada y controlada que es un patrón fundamental: while True con un break interior. Se usa cuando el programa debe repetir algo indefinidamente hasta que el usuario decida parar — exactamente lo que hace cualquier menú, cajero automático o aplicación interactiva.
while True:
respuesta = input("Escribe 'salir' para terminar: ")
if respuesta == "salir":
print("¡Hasta pronto!")
break
print(f"Has escrito: {respuesta}")Como True es siempre verdadero, el bucle no termina nunca por su condición: la única salida es el break. Esto no es un bucle infinito accidental, porque la salida existe y está clara; la clave es garantizar que el break es alcanzable.
Este patrón también resuelve del todo la validación de entradas que dejamos a medias en ficha_libro.py:
while True:
texto = input("Stock inicial del libro: ")
if texto.isdigit(): # ¿la cadena contiene solo dígitos?
stock = int(texto)
break
print("Entrada no válida. Escribe un número entero, por ejemplo: 4")
print(f"Stock registrado: {stock}")isdigit() es un método de las cadenas que devuelve True si todos sus caracteres son dígitos (lo veremos junto al resto de métodos de str en el módulo 4; de momento, úsalo tal cual). El bucle insiste hasta recibir un número válido, y solo entonces convierte y sale. Comparado con el while respuesta != ... de la lección anterior, esta forma evita duplicar el input() o inicializar variables artificiales: por eso es la preferida en Python.
match/case: selección por patrones (Python 3.10+)
Cuando un programa debe reaccionar de forma distinta según el valor concreto de una variable (la opción de un menú, un código de estado, un tipo de cliente), una cadena de if/elif funciona, pero desde Python 3.10 existe una alternativa más expresiva: la sentencia match.
opcion = "2"
match opcion:
case "1":
print("Mostrando el catálogo completo...")
case "2":
print("Buscador de libros...")
case "3":
print("Saliendo de Papyrus. ¡Hasta mañana, Ana!")
case _:
print("Opción no válida.")Cómo funciona:
match expresion:evalúa la expresión una vez.- Cada
case patron:se compara en orden; el primero que encaja ejecuta su bloque y la sentencia termina (no hay "caída" al siguiente caso, a diferencia delswitchde otros lenguajes: no necesitas ningúnbreakaquí). case _:es el comodín: encaja con cualquier cosa, y actúa como elelsefinal. Debe ir en último lugar.- Si ningún caso encaja y no hay comodín, simplemente no se ejecuta nada (sin error).
Además de valores literales, los patrones básicos permiten dos cosas muy útiles:
Alternativas con | (o barra vertical): un mismo caso para varios valores.
respuesta = input("¿Confirmar la venta? (s/n): ")
match respuesta:
case "s" | "S" | "si" | "sí":
print("Venta confirmada.")
case "n" | "N" | "no":
print("Venta cancelada.")
case _:
print("Respuesta no reconocida.")Condición extra con if (llamada guarda): el caso solo encaja si además se cumple la condición.
stock = 2
match stock:
case 0:
print("Agotado.")
case n if n <= 3:
print(f"Stock bajo: quedan {n}.")
case n:
print(f"Stock correcto: {n} unidades.")En case n if n <= 3:, el patrón n es un patrón de captura: encaja con cualquier valor y lo guarda en la variable n, pero la guarda if n <= 3 filtra cuándo se acepta. Es una puerta de entrada a los patrones estructurales más avanzados de match (desempaquetar listas, diccionarios u objetos), que cobrarán sentido cuando conozcas esas estructuras en los módulos 4 y 5.
¿match o if/elif? Guía rápida:
| Situación | Recomendación |
|---|---|
| Comparar una misma variable contra valores concretos (menús, códigos) | match gana en claridad |
Condiciones heterogéneas (stock > 0 and es_socio, rangos, varios datos) |
if/elif |
| Tu código debe funcionar en Python < 3.10 | if/elif obligatoriamente |
Proyecto: el menú interactivo de Papyrus
Juntemos todas las piezas de la lección en un script nuevo para el proyecto: menu.py, dentro de la carpeta papyrus (con el .venv activado, como siempre). Es el primer programa de Papyrus que conversa con Ana: se repite hasta que ella decide salir.
NOMBRE_TIENDA = "Papyrus"
catalogo = ["La Odisea", "Hamlet", "El Quijote"]
precios = [12.50, 9.95, 15.90]
stocks = [4, 6, 8]
print(f"Bienvenida a la gestión de {NOMBRE_TIENDA}")
while True:
print()
print("1. Ver catálogo")
print("2. Buscar un libro")
print("3. Salir")
opcion = input("Elige una opción (1-3): ")
match opcion:
case "1":
print(f"\n{'Título':<12} {'Precio':>8} {'Stock':>6}")
print("-" * 28)
for i, titulo in enumerate(catalogo):
print(f"{titulo:<12} {precios[i]:>6.2f} € {stocks[i]:>6}")
case "2":
buscado = input("Título a buscar: ")
if not buscado:
print("No has escrito nada.")
continue
for i, titulo in enumerate(catalogo):
if titulo.lower() == buscado.lower():
print(f"'{titulo}': {precios[i]:.2f} €, stock {stocks[i]}.")
break
else:
print(f"'{buscado}' no está en el catálogo.")
case "3":
print(f"Cerrando {NOMBRE_TIENDA}. ¡Hasta mañana, Ana!")
break
case _:
print("Opción no válida. Elige 1, 2 o 3.")Repasa cómo colabora cada herramienta:
while Truemantiene el menú vivo: tras cada operación, vuelve a mostrarse.match opciondespacha cada opción con un caso limpio, ycase _captura los errores de tecleo.- En la búsqueda,
continueactúa de guarda si Ana pulsa Intro sin escribir (truthiness de la cadena vacía, como en la lección 02-01): vuelve al menú sin ejecutar la búsqueda. for/break/else: elbreakcorta en cuanto hay coincidencia y elelsedel bucle cubre el caso "no encontrado" sin variables bandera. (lower()pasa el texto a minúsculas para que la búsqueda no distinga mayúsculas; más sobre métodos de cadenas en el módulo 4.)breaken elcase "3"es la única salida delwhile True: fíjate en que rompe el bucle exterior porquematchno es un bucle —breakycontinueatraviesan la sentenciamatchy actúan sobre el bucle que la contiene.
Una sesión de ejemplo:
Bienvenida a la gestión de Papyrus 1. Ver catálogo 2. Buscar un libro 3. Salir Elige una opción (1-3): 2 Título a buscar: hamlet 'Hamlet': 9.95 €, stock 6. 1. Ver catálogo 2. Buscar un libro 3. Salir Elige una opción (1-3): 3 Cerrando Papyrus. ¡Hasta mañana, Ana!
Este menú es deliberadamente sencillo: en el módulo 3 lo reorganizaremos con funciones (una por opción) y en el módulo 6 hará que el catálogo sobreviva entre sesiones guardándolo en archivos. El esqueleto, sin embargo, ya no cambiará: while True + match + break es el patrón estándar de las aplicaciones de consola.
Errores Comunes y Consejos
- Esperar que
breaksalga de todos los bucles anidados: solo sale del más interno. Si necesitas salir de varios niveles, suele ser señal de que ese código merece ser una función (módulo 3), dondereturnsale de todo de golpe. - Malinterpretar el
elsedel bucle como "si el bucle no se ejecutó ninguna vez": falso. Con una secuencia vacía, el bucle termina sinbreak... y elelsesí se ejecuta. Recuerda:else= "no hubobreak". - Usar
breakocontinuefuera de un bucle (por ejemplo, dentro de unifsuelto o esperando que corten unmatch):SyntaxError. Solo tienen sentido dentro defor/while. - Abusar de
continue: varias guardas seguidas al inicio del bucle son legibles;continues repartidos por mitad del bloque hacen el flujo difícil de seguir. Si te pasa, replantea las condiciones. - Olvidar el
breakde salida en unwhile True: entonces sí tienes un bucle infinito de verdad. Al escribirwhile True:, escribe elcase/ifde salida con subreakantes de rellenar el resto. - Escribir
case "1" | "2":cuando cada valor necesita lógica distinta: la alternativa|es para casos que comparten bloque. Si los bloques difieren, son casos separados. - Usar
matchcon Python antiguo: en versiones anteriores a 3.10 daSyntaxError. Comprueba tu versión conpython --version; si mantienes código para versiones viejas, usaif/elif. - Consejo:
passes útil mientras construyes, pero no debería llegar a la versión final del script salvo que ignorar el caso sea una decisión consciente — y entonces, coméntalo.
Ejercicios
Ejercicio 1: primera venta agotada
Dadas ventas_pendientes = [2, 1, 3, 2, 4] (unidades pedidas por varios clientes, en orden) y un stock = 6 de Hamlet, recorre los pedidos con un for: sirve cada pedido restando del stock e imprime Pedido de N uds. servido (quedan M). Si un pedido no puede servirse completo, imprime Stock insuficiente para el pedido de N uds. y deja de procesar pedidos en ese momento. Si todos se sirvieron, imprime al final Todos los pedidos servidos (pista: for/else).
Ejercicio 2: informe saltando descatalogados
Dadas las listas titulos = ["La Odisea", "Hamlet", "El Quijote", "Fausto"] y estados = ["activo", "activo", "descatalogado", "activo"], imprime un informe numerado (empezando en 1) solo de los libros activos, usando continue para saltar los descatalogados. La numeración debe ser consecutiva para los mostrados (1, 2, 3), no la posición original.
Ejercicio 3: cajero de Papyrus con match
Escribe un while True que pida a Ana un código de operación y reaccione con match:
"v"o"venta"→ pide un importe y súmalo a un acumuladorcaja(parte decaja = 0.0)."d"o"devolucion"→ pide un importe y réstalo decaja."t"→ imprime el total actual con dos decimales."x"→ imprime el total final y sale.- Cualquier otra cosa → mensaje de error.
Soluciones
Solución 1:
ventas_pendientes = [2, 1, 3, 2, 4]
stock = 6
for pedido in ventas_pendientes:
if pedido > stock:
print(f"Stock insuficiente para el pedido de {pedido} uds.")
break
stock -= pedido
print(f"Pedido de {pedido} uds. servido (quedan {stock})")
else:
print("Todos los pedidos servidos")Con stock 6, se sirven los pedidos de 2 y 1 (quedan 3), el de 3 (quedan 0) y el de 2 ya no puede servirse: break. Como hubo break, el else no se ejecuta — exactamente lo que queremos. Prueba a cambiar stock = 12 y verás el mensaje final.
Solución 2:
titulos = ["La Odisea", "Hamlet", "El Quijote", "Fausto"]
estados = ["activo", "activo", "descatalogado", "activo"]
numero = 0
for i, titulo in enumerate(titulos):
if estados[i] == "descatalogado":
continue
numero += 1
print(f"{numero}. {titulo}")La clave está en llevar un contador propio (numero) que solo se incrementa después de la guarda con continue: así los descatalogados no consumen número y la lista mostrada queda 1. La Odisea, 2. Hamlet, 3. Fausto.
Solución 3:
caja = 0.0
while True:
codigo = input("Operación (v/d/t/x): ")
match codigo:
case "v" | "venta":
caja += float(input("Importe de la venta: "))
case "d" | "devolucion":
caja -= float(input("Importe devuelto: "))
case "t":
print(f"Total en caja: {caja:.2f} €")
case "x":
print(f"Cierre. Total final: {caja:.2f} €")
break
case _:
print("Código no reconocido. Usa v, d, t o x.")El patrón completo de la lección en miniatura: while True como corazón del programa, | para aceptar sinónimos, case _ para errores de tecleo y un único break como salida. Como siempre, float() fallará si Ana escribe texto en el importe — la red de seguridad llegará en el módulo 7.
Conclusión
Tus bucles han dejado de ser trenes sin frenos: con break paras en cuanto encuentras lo que buscas, con continue te saltas lo que no interesa, con pass reservas el sitio de lo que está por escribir, con el else de los bucles detectas búsquedas sin éxito sin variables bandera, y con while True + break has construido el patrón universal de los programas interactivos. Además, match/case te da una forma moderna y legible de despachar opciones. Todo ello quedó ensamblado en menu.py, el primer programa de Papyrus que dialoga con Ana y el esqueleto de todo lo que viene.
Hay un último superpoder de este módulo. Habrás notado que muchos de nuestros bucles siguen el mismo patrón: recorrer una lista para construir otra (precios con IVA, títulos filtrados por stock...). Python tiene una sintaxis específica, compacta y elegantísima para ese patrón: las comprensiones de listas. Son la seña de identidad del código pythónico, y con ellas cerraremos el módulo en la próxima lección.
Curso de Programación en Python
Módulo 1: Introducción a Python
- Introducción a Python
- Configuración del Entorno de Desarrollo
- Sintaxis de Python y Tipos de Datos Básicos
- Variables y Constantes
- Entrada y Salida Básica
- Entornos Virtuales y Gestión de Paquetes
Módulo 2: Estructuras de Control
- Sentencias Condicionales
- Bucles: for y while
- Herramientas de Control de Flujo
- Comprensiones de Listas
Módulo 3: Funciones y Módulos
- Definición de Funciones
- Argumentos de Función
- Funciones Lambda
- Módulos y Paquetes
- Visión General de la Biblioteca Estándar
Módulo 4: Estructuras de Datos
Módulo 5: Programación Orientada a Objetos
Módulo 6: Manejo de Archivos
- Lectura y Escritura de Archivos
- Trabajo con Archivos CSV
- Manejo de Datos JSON
- Operaciones con Archivos y Directorios
Módulo 7: Manejo de Errores y Excepciones
- Introducción a las Excepciones
- Manejo de Excepciones
- Lanzamiento de Excepciones
- Excepciones Personalizadas
- Buenas Prácticas y Registro de Errores con logging
Módulo 8: Temas Avanzados
- Anotaciones de Tipos (type hints)
- Decoradores
- Generadores
- Administradores de Contexto
- Concurrencia: Hilos y Procesos
- Asyncio para Programación Asíncrona
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias con unittest
- Pruebas con pytest
- Desarrollo Guiado por Pruebas
- Técnicas de Depuración
- Uso de pdb para Depuración
Módulo 10: Desarrollo Web con Python
- Introducción al Desarrollo Web
- Fundamentos del Framework Flask
- Construcción de APIs REST con Flask
- Introducción a Django
- Construcción de Aplicaciones Web con Django
Módulo 11: Ciencia de Datos con Python
- Introducción a la Ciencia de Datos
- NumPy para Computación Numérica
- Pandas para Manipulación de Datos
- Matplotlib para Visualización de Datos
- Introducción al Aprendizaje Automático con scikit-learn
