Hasta ahora TareaFácil ha sido un guion de un solo acto: se lanza, hace preguntas, imprime una ficha y muere. Si Marta se equivoca al asignar la prioridad, no puede corregirla: tiene que volver a ejecutar el programa y teclearlo todo de nuevo. Si quiere volver a ver la ficha, lo mismo. Un programa así se demuestra, pero no se usa.
Lo que separa un script de una aplicación es un patrón sorprendentemente simple: el bucle principal. La aplicación muestra lo que sabe hacer, espera una orden, la ejecuta y vuelve a mostrarse, una y otra vez, hasta que el usuario decide marcharse. Es el esqueleto de casi todo el software interactivo que has usado nunca, desde un cajero automático hasta un editor de texto.
Lo mejor es que ya tienes todas las piezas: while True y break de la lección anterior, la validación insistente de 03-02, el despacho con if/elif o match de 03-01 y 03-03. Esta lección solo las monta juntas, y con ellas cierra el módulo.
Contenido
- Anatomía del bucle principal
while Trueconbreak, y la variante con bandera- Despachar la opción elegida
- Diseñar un menú que se entienda
- Validar la opción leída
- Confirmaciones para acciones destructivas
- TareaFácil v0.6: la aplicación completa
- Errores comunes y consejos
- Ejercicios
- Conclusión del módulo
- Anatomía del bucle principal
Un menú interactivo repite siempre el mismo ciclo de cinco pasos.
flowchart TD
A["Mostrar el menu"] --> B["Leer la opcion"]
B --> C{"Opcion valida ?"}
C -->|False| B
C -->|True| D{"Es la opcion de salir ?"}
D -->|True| F["Despedirse y terminar"]
D -->|False| E["Ejecutar la accion"]
E --> G["Pausa"]
G --> A
Los cinco pasos, con su traducción a código:
| Paso | Qué hace | Con qué se implementa |
|---|---|---|
| Mostrar | Pinta las opciones disponibles | Varios print() |
| Leer | Recoge la elección del usuario | input() |
| Validar | Rechaza lo que no es una opción | while de validación (03-02) |
| Ejecutar | Hace el trabajo pedido | if/elif o match (03-01, 03-03) |
| Volver | Regresa al principio | El propio bucle |
Y una regla de oro: el bucle no debe poder terminar por accidente. La única salida es la opción de salir; un error del usuario no puede cerrar la aplicación, porque en una aplicación real eso significa perder el trabajo.
while True con break, y la variante con bandera
while True con break, y la variante con banderaLa forma más directa es el bucle infinito controlado que viste en 03-03: while True: da vueltas indefinidamente y la opción de salir ejecuta un break.
while True:
print("1. Ver la tarea")
print("2. Salir")
opcion = input("Opcion: ").strip()
if opcion == "1":
print("Aqui iria la ficha de la tarea.")
elif opcion == "2":
print("Hasta luego.")
breakHay una segunda forma, con una bandera booleana que gobierna la condición: se inicializa en_ejecucion = True antes del bucle, la cabecera pasa a ser while en_ejecucion: y la opción de salir hace en_ejecucion = False en lugar del break. Las dos son correctas y verás ambas en código profesional.
while True + break |
Bandera en_ejecucion |
|
|---|---|---|
| Condición de salida | Escondida en el cuerpo | Visible en la cabecera |
| Salida inmediata | Sí: corta en el acto | No: termina la vuelta actual |
| Varios puntos de salida | Un break por sitio |
Poner la bandera a False |
| Legibilidad | Muy directa | Más declarativa |
La diferencia práctica importa: con break el bucle se corta en ese mismo instante; con la bandera, el resto del cuerpo de la vuelta actual sí se ejecuta antes de volver a comprobar la condición. Si después del despacho hay una pausa o un mensaje, con la bandera se verán también al salir. Usaremos break en TareaFácil, que es lo más habitual.
- Despachar la opción elegida
Despachar es dirigir la ejecución hacia el bloque que corresponde a la opción elegida. Con if/elif es inmediato: if opcion == "1": … elif opcion == "2": … y un else final para lo no reconocido. Pero como estamos comparando un valor contra alternativas concretas, este es exactamente el escenario para el que se diseñó match:
match opcion:
case "1":
print("Registrar tarea")
case "2":
print("Ver ficha")
case "3" | "c":
print("Cambiar prioridad")
case "5" | "s" | "salir":
print("Hasta luego")
case _:
print("Opcion no reconocida")Fíjate en la ventaja del patrón |: permite aceptar atajos con naturalidad, de modo que el usuario pueda teclear 5, s o salir para lo mismo. Con if/elif habría que escribir opcion in ("5", "s", "salir"), que también funciona pero se lee peor cuando hay muchas opciones.
Un aviso importante: aunque el match de un menú se lea muy bien, el break que sale del bucle no se puede sustituir por nada del match. break sigue perteneciendo al while, y funciona igual dentro de un case.
- Diseñar un menú que se entienda
El menú es la interfaz de tu programa, y un menú mal presentado convierte una aplicación correcta en una aplicación inutilizable. Seis reglas bastan:
- Numera las opciones y usa números consecutivos desde 1. Teclear un número es más rápido y menos propenso a error que escribir una palabra.
- Describe acciones, no sustantivos. «Cambiar la prioridad» dice qué va a pasar; «Prioridad», no.
- La opción de salir, siempre visible y siempre la última. El usuario debe poder marcharse sin adivinar cómo.
- Separa visualmente el menú del resultado con líneas de guiones o iguales. Recuerda el truco de la lección 02-03:
print("=" * ANCHO). - Pausa antes de repintar. Si el menú vuelve a dibujarse inmediatamente, el resultado de la acción desaparece de la vista antes de que dé tiempo a leerlo. La solución clásica es un
input("Pulsa Intro para continuar...")cuyo valor se ignora: solo sirve para esperar. - Repite el rango en el mensaje:
Elige una opcion (1-5):orienta mucho mejor queOpcion:.
ANCHO = 46
print("=" * ANCHO)
print(f"{'TareaFacil v0.6 - Estudio Alba':^{ANCHO}}")
print("=" * ANCHO)
print(" 1. Registrar / reemplazar la tarea")
print(" 2. Ver la ficha de la tarea")
print(" 3. Cambiar la prioridad")
print(" 4. Marcar como completada")
print(" 5. Salir")
print("-" * ANCHO)
- Validar la opción leída
La opción es un dato que viene de fuera, así que se le aplica el mismo trato que a cualquier otro: el marco de 02-04 —presencia, tipo, dominio— y el bucle insistente de 03-02.
OPCIONES = ("1", "2", "3", "4", "5")
opcion = input("Elige una opcion (1-5): ").strip()
while opcion not in OPCIONES:
opcion = input("Opcion no valida. Elige 1-5: ").strip()Tres decisiones de diseño que conviene entender:
- Comparamos textos, no números.
OPCIONEScontiene"1"y no1, así que no hace falta convertir. Si el usuario tecleahola, no hayValueErrorque valga: simplemente no está en el catálogo. Convertir conint()sin comprobar antes rompería el programa. - El
.strip()es imprescindible. Un espacio delante del número convierte" 1"en un dato inválido, y el usuario no vería por qué. - Intro sin escribir nada produce la cadena vacía, que tampoco está en
OPCIONES, así que el bucle vuelve a preguntar sin necesidad de ningún caso especial. Eso sí: conviene que el mensaje de reproche sea claro, porque un menú que responde en silencio parece colgado.
Si además quieres aceptar atajos como s para salir, basta con normalizar con .lower() y ampliar el catálogo: OPCIONES = ("1", "2", "3", "4", "5", "s").
- Confirmaciones para acciones destructivas
Una acción es destructiva si pierde información que el usuario no puede recuperar: reemplazar la tarea registrada, borrarla, salir de la aplicación sin guardar. Ante ellas, un programa educado pregunta.
confirmar = input("Seguro que quieres reemplazar la tarea? (s/n): ").strip().lower()
if confirmar == "s":
print("Reemplazando...")
else:
print("Operacion cancelada.")El detalle importante está en el else: comparamos con "s" y cualquier otra cosa cancela. Es deliberado. Ante la duda —un dedo torpe, un Intro accidental, una x sin querer—, la opción segura es no destruir nada; si hiciéramos if confirmar != "n":, cualquier tecla equivocada ejecutaría la acción irreversible. Y no abuses: si el programa pide confirmación para todo, el usuario aprende a teclear s sin leer y la confirmación deja de proteger. Confirma solo lo que de verdad no tiene vuelta atrás.
- TareaFácil v0.6: la aplicación completa
Ya está todo. TareaFácil deja de ser un guion y se convierte en una aplicación con cinco opciones sobre la única tarea que sabe gestionar. Fíjate en una variable nueva y decisiva: hay_tarea, la bandera que distingue «todavía no se ha registrado nada» de «hay una tarea que consultar o modificar».
# tareafacil.py - Estudio Alba
# Version 0.6: aplicacion con menu principal
ANCHO = 46
PRIORIDADES = ("alta", "media", "baja")
EQUIPO = ("Marta", "Luis", "Nuria")
OPCIONES = ("1", "2", "3", "4", "5")
# --- Estado de la aplicacion: los datos de LA tarea ---
hay_tarea = False
titulo = ""
responsable = ""
prioridad = ""
dias = 0
completada = False
while True:
print("\n" + "=" * ANCHO)
print(f"{'TareaFacil v0.6 - Estudio Alba':^{ANCHO}}")
print("=" * ANCHO)
print(" 1. Registrar / reemplazar la tarea")
print(" 2. Ver la ficha de la tarea")
print(" 3. Cambiar la prioridad")
print(" 4. Marcar como completada")
print(" 5. Salir")
print("-" * ANCHO)
opcion = input("Elige una opcion (1-5): ").strip()
while opcion not in OPCIONES:
opcion = input("Opcion no valida. Elige 1-5: ").strip()
if opcion == "1":
if hay_tarea:
confirmar = input(f"Ya existe '{titulo}'. Reemplazarla? (s/n): ").strip().lower()
if confirmar != "s":
print("Operacion cancelada.")
continue
titulo = input("Titulo : ").strip()
while titulo == "":
titulo = input("Vacio. Titulo : ").strip()
responsable = input("Responsable : ").strip().capitalize()
while responsable not in EQUIPO:
responsable = input("No es del equipo. Responsable: ").strip().capitalize()
prioridad = input("Prioridad : ").strip().lower()
while prioridad not in PRIORIDADES:
prioridad = input("No valida. Prioridad: ").strip().lower()
dias_texto = input("Dias (1-365) : ").strip()
while not dias_texto.isdigit() or not 1 <= int(dias_texto) <= 365:
dias_texto = input("Entero 1-365. Dias: ").strip()
dias = int(dias_texto)
completada = False
hay_tarea = True
print("Tarea registrada correctamente.")
elif opcion == "2":
if not hay_tarea:
print("Todavia no hay ninguna tarea registrada.")
else:
estado = "Completada" if completada else "Pendiente"
print("-" * ANCHO)
print(f"{'Titulo':<14}{titulo:>{ANCHO - 14}}")
print(f"{'Responsable':<14}{f'{responsable} ({prioridad})':>{ANCHO - 14}}")
print(f"{'Dias / estado':<14}{f'{dias} / {estado}':>{ANCHO - 14}}")
elif opcion == "3":
if not hay_tarea:
print("Todavia no hay ninguna tarea registrada.")
else:
nueva = input("Nueva prioridad (alta/media/baja): ").strip().lower()
while nueva not in PRIORIDADES:
nueva = input("No valida. Prioridad: ").strip().lower()
print(f"Prioridad de '{titulo}': {prioridad} -> {nueva}")
prioridad = nueva
elif opcion == "4":
if not hay_tarea:
print("Todavia no hay ninguna tarea registrada.")
elif completada:
print("La tarea ya estaba completada.")
else:
confirmar = input(f"Marcar '{titulo}' como completada? (s/n): ").strip().lower()
if confirmar == "s":
completada = True
print("Tarea completada. Buen trabajo.")
else:
print("Operacion cancelada.")
elif opcion == "5":
confirmar = input("Seguro que quieres salir? (s/n): ").strip().lower()
if confirmar == "s":
print("Hasta luego. TareaFacil se cierra.")
break
input("\nPulsa Intro para volver al menu...")Cuatro detalles del código merecen comentario:
- El estado vive fuera del bucle. Las variables
titulo,prioridad,hay_tarea… se declaran antes delwhiley sobreviven a todas las vueltas. Si estuvieran dentro, cada vuelta las reiniciaría y la aplicación olvidaría la tarea nada más pintar el menú. Es el mismo principio que el acumulador de 03-02. hay_tareaprotege las opciones 2, 3 y 4. Sin esa comprobación, elegir «Ver la ficha» antes de registrar nada mostraría una ficha vacía y absurda. Una aplicación seria nunca ofrece un dato que no tiene.- El
continuede la opción 1 cancela el reemplazo y vuelve al menú saltándose el resto de la vuelta, incluida la pausa final. Es exactamente elcontinuede 03-03 aplicado al bucle principal. - Salir también se confirma, porque en la versión actual cerrar el programa pierde la tarea. Y solo
"s"sale: cualquier otra tecla sigue en la aplicación.
Una sesión de trabajo típica de Marta:
Elige una opcion (1-5): 9 Opcion no valida. Elige 1-5: 1 Titulo : Cartel feria del libro Responsable : nuria Prioridad : urgentisima No valida. Prioridad: alta Dias (1-365) : 4 Tarea registrada correctamente. Pulsa Intro para volver al menu...
Después, la opción 3 cambia la prioridad a media, la 2 muestra la ficha actualizada y la 5 pide confirmación antes de cerrar. La misma ejecución del programa hace todo eso, que es justo lo que no podíamos hacer en ninguna versión anterior.
Errores Comunes y Consejos
Declarar el estado dentro del bucle. El error número uno de los menús: la aplicación «olvida» los datos en cada vuelta. Todo lo que deba sobrevivir va antes del while.
Olvidar el break en la opción de salir. El menú se vuelve inescapable y solo queda Ctrl+C. Compruébalo siempre lo primero.
Convertir la opción con int() sin validar. Si el usuario teclea una letra, ValueError y programa muerto. Trabaja con la opción como texto y compárala contra un catálogo.
No pausar antes de repintar. El resultado aparece y desaparece en el mismo parpadeo; un input("Pulsa Intro...") lo resuelve. Y no ofrezcas opciones imposibles, como consultar una tarea que aún no existe: una bandera de estado como hay_tarea y un mensaje claro lo evitan.
Confirmar con != "n". Cualquier tecla despistada ejecuta la acción destructiva. Confirma siempre con == "s" y deja que todo lo demás cancele.
Consejo: guarda el número de opciones en una constante. Con OPCIONES arriba del fichero, añadir una opción es tocar dos sitios (la constante y el menú), no rebuscar por todo el programa.
Consejo: prueba tu menú como un usuario hostil. Teclea letras, espacios, Intro a secas, números fuera de rango, y elige la opción 2 nada más arrancar. Cada cosa que rompa el programa es una validación que falta.
Ejercicios
Ejercicio 1: Detectar los fallos
Este menú tiene tres defectos graves. Encuéntralos y explica qué le pasa al usuario en cada caso.
while True:
contador_usos = 0
print("1. Ver 2. Salir")
opcion = int(input("Opcion: "))
if opcion == 1:
contador_usos += 1
print(f"Consultas: {contador_usos}")
elif opcion == 2:
print("Adios")Ejercicio 2: Menú de la agenda del equipo
Escribe agenda_equipo.py, una aplicación con bucle principal y menú de cuatro opciones que gestione una sola nota de agenda: 1) escribir/reemplazar la nota (texto no vacío y día de la semana entre 1 y 5), 2) ver la nota, 3) borrar la nota (con confirmación), 4) salir. Usa una bandera hay_nota, valida la opción con un while, despacha con match y pausa antes de repintar.
Ejercicio 3: Contador de operaciones
Amplía TareaFácil v0.6 para que lleve la cuenta de cuántas veces se ha usado cada opción durante la sesión y muestre un resumen al salir, con el patrón contador de 03-02. Indica dónde deben declararse los contadores y por qué.
Soluciones
Solución 1.
| Defecto | Qué le pasa al usuario |
|---|---|
contador_usos = 0 está dentro del bucle |
Se reinicia en cada vuelta: el contador siempre muestra 1 |
int(input(...)) sin validar |
Si teclea una letra o pulsa Intro, el programa muere con ValueError |
La opción 2 no tiene break |
Imprime «Adios» y vuelve al menú: es imposible salir |
Los tres tienen la misma raíz: el bucle se escribió pensando solo en el usuario que hace lo previsto. La versión corregida saca el contador fuera del while, lee la opción como texto validado contra un catálogo y añade el break.
Solución 2.
# agenda_equipo.py - Estudio Alba
OPCIONES = ("1", "2", "3", "4")
DIAS = ("1", "2", "3", "4", "5")
hay_nota = False
nota = ""
dia = ""
while True:
print(" 1. Escribir / reemplazar la nota 2. Ver la nota")
print(" 3. Borrar la nota 4. Salir")
opcion = input("Elige una opcion (1-4): ").strip()
while opcion not in OPCIONES:
opcion = input("Opcion no valida. Elige 1-4: ").strip()
match opcion:
case "1":
nota = input("Texto de la nota: ").strip()
while nota == "":
nota = input("No puede estar vacia. Nota: ").strip()
dia = input("Dia laborable (1-5): ").strip()
while dia not in DIAS:
dia = input("Debe ser de 1 a 5. Dia: ").strip()
hay_nota = True
print("Nota guardada.")
case "2":
if hay_nota:
print(f"Dia {dia}: {nota}")
else:
print("No hay ninguna nota escrita.")
case "3":
if not hay_nota:
print("No hay nada que borrar.")
else:
confirmar = input(f"Borrar '{nota}'? (s/n): ").strip().lower()
if confirmar == "s":
hay_nota = False
nota = ""
print("Nota borrada.")
else:
print("Operacion cancelada.")
case "4":
print("Hasta luego.")
break
input("\nPulsa Intro para continuar...")Observa que el break de case "4" funciona con normalidad: pertenece al while, no al match. Y que borrar no consiste solo en poner hay_nota = False: conviene limpiar también nota, para que ningún camino del programa pueda mostrar un dato que el usuario cree eliminado.
Solución 3.
Los contadores —uno por opción— deben declararse antes del while, junto al resto del estado, exactamente por la misma razón que hay_tarea: si estuvieran dentro, se reiniciarían en cada vuelta y siempre valdrían 1. Se incrementan al principio del bloque de cada opción, y el resumen se imprime justo antes del break:
usos_registrar = 0
usos_ver = 0
usos_prioridad = 0
usos_completar = 0
while True:
# ... menu y validacion de la opcion ...
if opcion == "1":
usos_registrar += 1
# ... registro de la tarea ...
elif opcion == "5":
confirmar = input("Seguro que quieres salir? (s/n): ").strip().lower()
if confirmar == "s":
print(f"Resumen: {usos_registrar} registros, {usos_ver} consultas,")
print(f"{usos_prioridad} cambios de prioridad, {usos_completar} completadas.")
breakY aquí asoma un problema que ya no es de bucles: cuatro variables casi idénticas para contar cuatro cosas casi idénticas. Es el mismo síntoma que veremos en la conclusión, y su solución está en el módulo 5.
Conclusión del módulo
TareaFácil ha llegado a la versión 0.6 y ya es una aplicación de verdad. Muestra un menú, espera órdenes, valida lo que recibe, ejecuta la acción pedida, avisa cuando algo no se puede hacer, confirma lo irreversible y solo se cierra cuando el usuario lo dice. Marta puede registrar la tarea, consultarla, cambiarle la prioridad y marcarla como completada sin volver a lanzar el programa.
Y con ello se cierra el módulo 3. Sabes decidir con if, elif y else, con condiciones compuestas, anidamiento y cláusulas de guarda (03-01). Sabes repetir con while y for, con range() y sus tres formas, y con los cuatro patrones clásicos —contador, acumulador, máximo y bandera— (03-02). Sabes controlar el flujo con precisión mediante break, continue, la cláusula else de los bucles, los bucles anidados y match (03-03). Y sabes montar todo eso en un bucle de aplicación con menú, validación, despacho, pausa y confirmaciones (03-04). Con estas tres estructuras —secuencia, decisión y repetición— puedes expresar cualquier algoritmo que exista: es un resultado clásico de la informática, y ya lo tienes entero.
Pero mira la versión 0.6 con ojos críticos, porque sus dos límites son el mapa de lo que viene. El primero: el código se ha vuelto largo y repetitivo. Casi cien líneas en un solo bloque, con if not hay_tarea: escrito tres veces, cuatro bucles de validación que se parecen como gotas de agua y un menú que crecerá cada vez que añadamos una función. Todo eso pide a gritos poder dar nombre a un trozo de código y reutilizarlo: escribir una vez pedir_prioridad() y llamarla desde donde haga falta. Eso son las funciones, y son el módulo 4 entero.
El segundo: solo cabe una tarea. Toda la aplicación gira alrededor de un único juego de variables —titulo, responsable, prioridad, dias—, y registrar una tarea nueva significa destruir la anterior. Por eso la opción 1 pide confirmación. Pero el estudio no tiene una tarea: tiene decenas, y Marta necesita verlas todas, filtrarlas por responsable y ordenarlas por prioridad. Para eso hacen falta estructuras capaces de guardar muchos valores bajo un solo nombre: las listas, que llegan en Listas y arreglos, y con ellas el for que recorre una colección de tareas de verdad.
Dicho de otro modo: sabes construir la maquinaria de un programa, y ahora toca aprender a organizarla y a darle datos con los que valga la pena trabajar. Nos vemos en el módulo 4, con las funciones.
Fundamentos de la Programación
Módulo 1: Introducción a la Programación
- ¿Qué es la programación?
- Historia de la programación
- Lenguajes de programación
- Entornos de desarrollo
- Del problema al algoritmo
Módulo 2: Conceptos Básicos
- Variables y tipos de datos
- Operadores y expresiones
- Entrada y salida de datos
- Conversión de tipos y validación de datos
Módulo 3: Estructuras de Control
Módulo 4: Funciones y Procedimientos
- Definición y uso de funciones
- Parámetros y retorno de valores
- Ámbito de variables
- Descomponer un programa en funciones
- Funciones como valores: lambda y orden superior
Módulo 5: Estructuras de Datos
- Listas y arreglos
- Cadenas de caracteres
- Diccionarios y conjuntos
- Tuplas y estructuras anidadas
- Guardar datos en archivos: texto, CSV y JSON
Módulo 6: Algoritmos Básicos
Módulo 7: Objetos y Organización del Código
- De los datos a los objetos: clases e instancias
- Atributos, métodos y constructor
- Colecciones de objetos
- Módulos, paquetes e importaciones
Módulo 8: Buenas Prácticas y Herramientas
- Documentación y comentarios
- Depuración y manejo de errores
- Control de versiones
- Pruebas automatizadas
- Estilo, legibilidad y refactorización
