Un menú interactivo en el terminal permite presentar opciones, recibir una elección y ejecutar funciones sin obligar al usuario a recordar comandos. Es un proyecto ideal para practicar entrada de datos, bucles, condiciones, diccionarios, funciones y manejo de errores. Aunque parece sencillo, un menú bien diseñado debe validar entradas, permitir volver atrás, evitar bucles bloqueados y separar la interfaz de la lógica del programa.
Para reforzar los conceptos utilizados, consulta la guía de input() en Python, el artículo sobre bucles while, el tutorial de funciones, la explicación de diccionarios y la guía de try y except.
Las referencias externas principales son la documentación oficial de input() y el tutorial oficial de control de flujo de Python.
La estructura básica del menú
while True:
print("\nMENÚ PRINCIPAL")
print("1. Saludar")
print("2. Mostrar fecha")
print("0. Salir")
opcion = input("Elige una opción: ").strip()
if opcion == "1":
print("Hola")
elif opcion == "2":
print("Aquí iría la fecha")
elif opcion == "0":
print("Hasta luego")
break
else:
print("Opción inválida")
El bucle mantiene activo el programa. break termina cuando el usuario elige salir. Comparar strings evita una conversión innecesaria que podría fallar con letras.
Separar cada acción en una función
from datetime import datetime
def saludar():
nombre = input("Tu nombre: ").strip()
if not nombre:
print("El nombre no puede estar vacío")
return
print(f"Hola, {nombre}")
def mostrar_fecha():
ahora = datetime.now()
print(ahora.strftime("%d/%m/%Y %H:%M"))
Las funciones reducen el tamaño del bloque principal y facilitan pruebas. Cada acción debe recibir datos y devolver resultados cuando sea posible, en lugar de depender de muchas variables globales.
Crear una función para pausar
def pausar():
input("\nPulsa Enter para continuar...")
La pausa permite leer el resultado antes de volver al menú. No es necesaria en todos los terminales, pero mejora la experiencia cuando la pantalla se limpia entre acciones.
Limpiar la pantalla de forma multiplataforma
import os
def limpiar_pantalla():
comando = "cls" if os.name == "nt" else "clear"
os.system(comando)
os.system() es suficiente para este uso controlado, porque el comando no proviene del usuario. No construyas comandos del sistema concatenando entradas no confiables. También puedes evitar limpiar la pantalla y simplemente imprimir separadores.
Organizar opciones con un diccionario
def salir():
print("Programa finalizado")
return False
ACCIONES = {
"1": saludar,
"2": mostrar_fecha,
"0": salir,
}
Después, el menú puede buscar la función:
def ejecutar_menu():
continuar = True
while continuar:
mostrar_menu()
opcion = input("Elige una opción: ").strip()
accion = ACCIONES.get(opcion)
if accion is None:
print("Opción inválida")
pausar()
continue
resultado = accion()
if resultado is False:
continuar = False
else:
pausar()
Este enfoque evita una cadena extensa de if y facilita añadir nuevas opciones. Utiliza is None para comprobar que no se encontró una acción.
Mostrar el menú desde una estructura
OPCIONES = {
"1": ("Saludar", saludar),
"2": ("Mostrar fecha", mostrar_fecha),
"0": ("Salir", salir),
}
def mostrar_menu():
print("\n=== MENÚ PRINCIPAL ===")
for clave, (texto, _) in OPCIONES.items():
print(f"{clave}. {texto}")
La misma estructura guarda el texto y la función, evitando mantener dos listas separadas que podrían desincronizarse.
Validar números correctamente
Una acción puede necesitar un entero:
def leer_entero(mensaje, minimo=None, maximo=None):
while True:
texto = input(mensaje).strip()
try:
valor = int(texto)
except ValueError:
print("Escribe un número entero")
continue
if minimo is not None and valor < minimo:
print(f"El valor mínimo es {minimo}")
continue
if maximo is not None and valor > maximo:
print(f"El valor máximo es {maximo}")
continue
return valor
Centralizar la validación evita repetir try/except en todas las opciones. Los operadores están escapados en HTML, pero el código ejecutado utiliza los símbolos normales.
Crear un submenú
def menu_configuracion():
while True:
print("\nCONFIGURACIÓN")
print("1. Cambiar idioma")
print("2. Cambiar tema")
print("0. Volver")
opcion = input("Opción: ").strip()
if opcion == "1":
print("Idioma actualizado")
elif opcion == "2":
print("Tema actualizado")
elif opcion == "0":
return
else:
print("Opción inválida")
Un return sale del submenú y regresa a la función que lo llamó. Evita usar exit() para navegar entre menús, porque termina todo el proceso.
Guardar datos en JSON
import json
from pathlib import Path
RUTA_CONFIG = Path("config.json")
def guardar_configuracion(config):
temporal = RUTA_CONFIG.with_suffix(".tmp")
temporal.write_text(
json.dumps(config, ensure_ascii=False, indent=2),
encoding="utf-8",
)
temporal.replace(RUTA_CONFIG)
La escritura temporal reduce el riesgo de dejar un archivo incompleto. Valida los datos al cargar y utiliza valores predeterminados cuando el archivo no exista.
Manejar errores sin cerrar el programa
def ejecutar_accion(accion):
try:
return accion()
except FileNotFoundError as error:
print(f"No se encontró el archivo: {error.filename}")
except PermissionError:
print("No tienes permiso para completar la operación")
except Exception as error:
print(f"Ocurrió un error inesperado: {error}")
En producción, registra el traceback con logging. Captura excepciones concretas antes de una excepción general. No ocultes errores silenciosamente.
Añadir confirmaciones
def confirmar(mensaje):
respuesta = input(f"{mensaje} [s/N]: ").strip().casefold()
return respuesta in {"s", "si", "sí"}
Utiliza confirmaciones antes de borrar archivos, sobrescribir datos o cerrar con cambios pendientes. Una respuesta vacía puede representar la opción segura, en este caso “no”.
Permitir atajos y nombres
alias = {
"salir": "0",
"fecha": "2",
"ayuda": "3",
}
entrada = input("Opción: ").strip().casefold()
opcion = alias.get(entrada, entrada)
Los alias mejoran la usabilidad sin complicar el flujo. Documenta los comandos disponibles y evita nombres ambiguos.
Probar el menú
La función input() dificulta las pruebas cuando está mezclada con toda la lógica. Separa funciones puras:
def resolver_opcion(opcion, acciones):
return acciones.get(opcion)
def test_resolver_opcion():
acciones = {"1": saludar}
assert resolver_opcion("1", acciones) is saludar
assert resolver_opcion("9", acciones) is None
También puedes usar mocks para simular entradas y capturar salidas. Las acciones que modifican archivos deberían probarse en directorios temporales.
Evitar un menú demasiado grande
Cuando existen decenas de opciones, divide el programa en módulos, crea submenús y considera una interfaz de línea de comandos con argparse. Un menú interactivo funciona bien para herramientas locales y educativas, pero no siempre es la mejor interfaz para automatización.
Ejemplo completo de estructura
def ejecutar():
while True:
limpiar_pantalla()
mostrar_menu()
opcion = input("Opción: ").strip()
dato = OPCIONES.get(opcion)
if dato is None:
print("Opción inválida")
pausar()
continue
_, accion = dato
if ejecutar_accion(accion) is False:
break
if __name__ == "__main__":
ejecutar()
La protección if __name__ == "__main__" evita iniciar el menú cuando el archivo se importa desde pruebas u otros módulos.
Errores frecuentes
Los problemas más comunes son olvidar la opción de salida, convertir la entrada a entero antes de validarla, repetir toda la lógica dentro de un bloque gigante, usar variables globales, llamar funciones al construir el diccionario y limpiar la pantalla con comandos proporcionados por el usuario. También es frecuente utilizar bucles anidados sin una forma clara de volver.
Conclusión
Un buen menú de terminal combina un bucle controlado, funciones pequeñas, un mapa de opciones y validación reutilizable. Empieza con pocas acciones, separa presentación y lógica y añade manejo de errores. Cuando la herramienta crezca, utiliza módulos, pruebas y submenús. Así obtendrás una interfaz sencilla, mantenible y segura para tus scripts de Python.






