Vas a construir la lógica de una pequeña lista de tareas para terminal. No empezaremos por el menú. Primero haremos que el programa sepa representar, añadir y completar tareas.
Tu versión mínima solo necesita tres acciones: añadir, listar y marcar una tarea como terminada. Todo lo demás es opcional.
Construir una aplicación mínima por checkpoints: representar, añadir/listar, completar y persistir.
Paso 1: decide qué información necesita una tarea
Antes de escribir funciones, define el dato. Para este proyecto una tarea puede ser simplemente un diccionario con dos piezas: el texto y si está terminada.
tarea = {
"texto": "Repasar funciones",
"hecha": False,
}
print(tarea["texto"])
print(tarea["hecha"])Este modelo es deliberadamente pequeño. No añadas fecha, prioridad, etiquetas o identificadores hasta que exista una necesidad concreta. Cada campo extra añade decisiones y posibles errores.
¿Por qué un diccionario y no una clase?
Porque ahora mismo solo necesitamos guardar dos datos. Una clase podría ser una buena evolución más adelante, pero no mejora automáticamente esta primera versión.
Paso 2: construye el corazón antes de construir el menú
El programa debería poder trabajar aunque todavía no exista input(). Eso permite probar las reglas con datos controlados.
def agregar(tareas, texto):
tareas.append({"texto": texto, "hecha": False})
def completar(tareas, posicion):
tareas[posicion]["hecha"] = True
def pendientes(tareas):
return [t for t in tareas if not t["hecha"]]
tareas = []
agregar(tareas, "Practicar listas")
agregar(tareas, "Leer 10 minutos")
completar(tareas, 0)
print(len(pendientes(tareas)))Observa la ventaja: si completar() falla, no necesitas investigar un menú, un archivo y cinco opciones. El error está en una pieza pequeña.
Paso 3: deja que la interfaz traduzca lo que escribe la persona
La terminal produce texto. Tu lógica trabaja mejor con valores ya preparados. Por eso conviene que la interfaz haga la traducción y luego llame a las funciones del proyecto.
def procesar_opcion(opcion, tareas):
if opcion == "1":
return "Añadir"
if opcion == "2":
return f"Hay {len(tareas)} tareas"
return "Opción no válida"
print(procesar_opcion("2", [{"texto": "Estudiar", "hecha": False}]))Cuando añadas input(): recuerda que siempre devuelve texto. Convierte a entero solo aquello que realmente necesite ser un número, como una posición elegida por el usuario.
Paso 4: añade persistencia cuando el núcleo ya funciona
Guardar en JSON es una capa nueva, no una razón para reescribir el proyecto. Dos funciones pequeñas pueden encargarse de leer y guardar la lista completa.
import json
from pathlib import Path
RUTA = Path("tareas.json")
def guardar(tareas):
RUTA.write_text(json.dumps(tareas, ensure_ascii=False), encoding="utf-8")
def cargar():
if not RUTA.exists():
return []
return json.loads(RUTA.read_text(encoding="utf-8"))No necesitas ejecutar este bloque para entender la idea: guardar() transforma la lista en texto JSON y cargar() recupera una lista o devuelve una vacía si todavía no existe archivo.
Construye el MVP en cuatro checkpoints
Cada checkpoint conserva lo anterior. No añadas colores, prioridades, fechas ni usuarios hasta que estos cuatro funcionen.
Checkpoint 1 · representar una tarea
Completa el diccionario mínimo de una tarea: texto + estado.
Comprar pan False
La salida aparecerá aquí.
Necesito una pista
- Añade la clave
"hecha"con valor booleanoFalse.
Ver una solución razonada
tarea = {"texto": "Comprar pan", "hecha": False}
print(tarea["texto"])
print(tarea["hecha"])Checkpoint 2 · añadir y listar
Implementa agregar() y deja que el listado recorra la colección.
Comprar pan Estudiar Python
La salida aparecerá aquí.
Necesito una pista
- Añade a la lista un diccionario con
textoyhecha=False.
Ver una solución razonada
def agregar(tareas, texto):
tareas.append({"texto": texto, "hecha": False})
tareas = []
agregar(tareas, "Comprar pan")
agregar(tareas, "Estudiar Python")
for tarea in tareas:
print(tarea["texto"])Checkpoint 3 · completar sin destruir datos
Marca una tarea como hecha conservando su texto.
Comprar pan | True
La salida aparecerá aquí.
Necesito una pista
- Accede a la tarea por posición y cambia solo la clave
"hecha".
Ver una solución razonada
def completar(tareas, posicion):
tareas[posicion]["hecha"] = True
tareas = [{"texto": "Comprar pan", "hecha": False}]
completar(tareas, 0)
print(tareas[0]["texto"], "|", tareas[0]["hecha"])Checkpoint 4 · persistir y recuperar
Serializa la lista a JSON y vuelve a cargarla. Así compruebas la persistencia sin depender de un menú.
[{'texto': 'Comprar pan', 'hecha': True}]La salida aparecerá aquí.
Necesito una pista
- Usa
json.dumps(tareas)y despuésjson.loads(texto).
Ver una solución razonada
import json
tareas = [{"texto": "Comprar pan", "hecha": True}]
texto = json.dumps(tareas)
recuperadas = json.loads(texto)
print(recuperadas)Si el proyecto se rompe, localiza primero la capa
- Los datos están mal: inspecciona la lista y un diccionario concreto.
- La regla está mal: llama a la función directamente sin menú.
- La entrada falla: imprime temporalmente lo que devuelve
input(). - No se conserva al reiniciar: revisa solo
cargar()yguardar().
Esta división evita el pensamiento “todo está roto”. Casi siempre hay una capa concreta que está produciendo el resultado inesperado.
La versión 1 está terminada mucho antes de ser perfecta
Da el proyecto por terminado cuando puedas añadir dos tareas, listarlas, marcar una como hecha y volver a obtener el mismo estado después de guardarlo y cargarlo.
Después, si te apetece, añade una sola mejora: borrar tareas, prioridades o búsqueda. No intentes convertir el ejercicio en una aplicación profesional.