TareaFácil v0.10 hace casi todo lo que Marta necesita: registra las tareas del estudio, las lista ordenadas por prioridad, permite cambiarlas, completarlas y ver el reparto de trabajo. Y sin embargo, cada vez que Marta cierra el programa todo se borra. Al día siguiente vuelve a encontrarse la agenda vacía. Es el último cabo suelto del módulo y, para un programa que aspira a ser útil, el más grave: sin persistencia no hay herramienta, hay demostración.
Esta lección resuelve exactamente eso. Aprenderás a escribir datos en un fichero y a recuperarlos, en los tres formatos que cubren la práctica totalidad de los casos: texto plano para lo más simple, CSV para lo que acabará en una hoja de cálculo y JSON para guardar estructuras completas como nuestra lista de diccionarios. Al terminar, la agenda de Estudio Alba seguirá ahí mañana por la mañana.
Contenido
- Memoria y disco: por qué se pierde todo
- Rutas, directorio de trabajo y
pathlib - Abrir ficheros: modos,
withy codificación - Texto plano: escribir y leer
- CSV: datos en filas y columnas
- JSON: estructuras completas
- Texto, CSV o JSON: cuál usar
- Comprobar si el fichero existe
- TareaFácil v0.11: la agenda sobrevive
- Errores comunes y consejos
- Ejercicios
- Conclusión del módulo
- Memoria y disco: por qué se pierde todo
Todo lo que has creado hasta ahora —variables, listas, diccionarios— vive en la memoria RAM, que es rápida y volátil: cuando el proceso termina, el sistema operativo recupera ese espacio y el contenido desaparece. El disco es lento en comparación, pero persistente: lo que se escribe ahí sigue existiendo después de apagar el ordenador.
graph LR
A["Programa en ejecucion<br/>agenda en memoria RAM"] -->|guardar: escribir| B["Fichero en disco<br/>tareas.json"]
B -->|cargar: leer| A
A -->|cerrar el programa| C["La memoria se libera<br/>los datos se pierden"]
Persistencia es el nombre de ese puente, y tiene dos direcciones con nombre propio: serializar (o guardar) es traducir una estructura de Python a texto o bytes que puedan escribirse, y deserializar (o cargar) es leer ese texto y reconstruir la estructura original. Un fichero es, en el fondo, una secuencia de bytes con un nombre; lo que llamamos «formato» —texto, CSV, JSON— no es más que un acuerdo sobre cómo se organizan esos bytes para poder volver a interpretarlos.
- Rutas, directorio de trabajo y
pathlib
pathlibPara abrir un fichero hay que decir dónde está. Una ruta relativa (tareas.json, datos/tareas.json) se interpreta a partir del directorio de trabajo, que es la carpeta desde la que se lanzó el programa. Una ruta absoluta (/home/marta/alba/tareas.json en Linux o macOS, C:\Users\marta\alba\tareas.json en Windows) empieza en la raíz y no depende de nada.
La relativa es portátil —funciona en cualquier ordenador siempre que el fichero esté junto al programa— y la absoluta no, así que salvo casos muy concretos se prefiere la relativa. El módulo pathlib de la biblioteca estándar ofrece Path, un objeto que representa una ruta y funciona igual en todos los sistemas operativos:
from pathlib import Path
ruta = Path("tareas.json")
print(ruta.name, ruta.suffix) # tareas.json .json
print(ruta.exists()) # False si todavia no existe
print(Path.cwd()) # el directorio de trabajo actual
destino = Path("datos") / "tareas.json" # el operador / une rutasEse / que une un Path con un texto es la forma moderna y segura de construir rutas: evita tener que escribir barras a mano y funciona en Windows sin cambiar nada. Como aquí el fichero vivirá junto al programa, usaremos rutas relativas simples.
- Abrir ficheros: modos,
with y codificación
with y codificaciónLa función open(ruta, modo, encoding=...) abre un fichero y devuelve un objeto sobre el que leer o escribir. El modo decide qué se puede hacer y qué pasa con lo que hubiera dentro:
| Modo | Significa | Si el fichero existe | Si no existe |
|---|---|---|---|
"r" |
Leer (por defecto) | Lo lee | FileNotFoundError |
"w" |
Escribir | Lo vacía por completo | Lo crea |
"a" |
Añadir al final | Escribe detrás | Lo crea |
"x" |
Crear en exclusiva | FileExistsError |
Lo crea |
Retén el aviso sobre "w": abrir un fichero en modo escritura borra todo su contenido en el acto, antes incluso de que escribas nada. Es la causa número uno de pérdidas de datos de los programas de principiante. Si lo que quieres es añadir, el modo es "a".
Un fichero abierto hay que cerrarlo para que el sistema vuelque lo escrito y libere el recurso. En vez de hacerlo a mano, se usa siempre el bloque with:
with open("tareas.txt", "w", encoding="utf-8") as fichero:
fichero.write("Cartel feria del libro\n")
# aqui el fichero ya esta cerrado, pase lo que pase dentro del bloquewith garantiza el cierre incluso si se produce un error dentro del bloque, y es la razón por la que jamás verás en código moderno un open() suelto seguido de close(). Todo lo que se hace con el fichero va dentro, indentado. El otro parámetro imprescindible es encoding="utf-8". Como viste en 05-02, el texto se guarda en disco como bytes, y la codificación es la tabla de traducción. Si no lo indicas, Python usa la codificación por defecto del sistema, que en algunos Windows no es UTF-8: el fichero se leerá mal en otro ordenador y las tildes aparecerán como PanaderÃa. Escríbelo siempre, tanto al leer como al escribir.
- Texto plano: escribir y leer
El formato más simple: líneas de texto, sin más estructura que la que tú le des.
titulos = ["Cartel feria del libro", "Menu Panaderia Sole", "Logotipo Vidal"]
with open("titulos.txt", "w", encoding="utf-8") as f:
for titulo in titulos:
f.write(titulo + "\n") # write NO anade el salto de linea
f.writelines([t + "\n" for t in titulos]) # writelines tampoco lo anade
with open("titulos.txt", "a", encoding="utf-8") as f:
f.write("Flyer verano\n") # modo 'a': se anade al final, sin borrarLa diferencia entre write y print es exactamente esa: print añade un salto de línea al final y write no. Si olvidas el "\n", todo el fichero acaba en una única línea gigante. Para leer hay tres formas:
with open("titulos.txt", "r", encoding="utf-8") as f:
contenido = f.read() # todo el fichero en una sola cadena
with open("titulos.txt", "r", encoding="utf-8") as f:
lineas = f.readlines() # lista de cadenas, cada una con su \n
with open("titulos.txt", "r", encoding="utf-8") as f:
for linea in f: # la forma preferente: linea a linea
print(linea.rstrip()) # rstrip quita el \n del finalRecorrer el fichero directamente con for linea in f: es lo recomendable: no carga el fichero entero en memoria, así que funciona igual con tres líneas que con tres millones. Y casi siempre hay que aplicar .rstrip(), porque cada línea leída conserva su salto de línea final; sin él, comparar linea == "Marta" fallaría siempre por culpa de ese "\n" invisible.
Combinando esto con split de 05-02 ya se puede guardar una tarea entera por línea:
with open("tareas.txt", "w", encoding="utf-8") as f:
for t in agenda:
f.write(f"{t['titulo']};{t['responsable']};{t['prioridad']};{t['dias']}\n")
with open("tareas.txt", "r", encoding="utf-8") as f:
for linea in f:
titulo, responsable, prioridad, dias = linea.rstrip().split(";")Funciona, y ya se le ven las costuras: si un título contiene un punto y coma, el split produce cinco campos y el desempaquetado revienta. Ese problema exacto es el que resuelve el formato siguiente.
- CSV: datos en filas y columnas
CSV (comma-separated values) es un formato de texto para tablas: una línea por fila y un separador entre campos. Su gran virtud es que Excel, LibreOffice y Google Sheets lo abren directamente, así que es la forma habitual de entregar datos a alguien que no programa.
titulo;responsable;prioridad;dias;completada Cartel feria del libro;Marta;alta;3;False Menu Panaderia Sole;Luis;media;5;True
La primera línea es la cabecera, con los nombres de las columnas. El separador estándar es la coma, pero en España es habitual el punto y coma, porque nuestras hojas de cálculo usan la coma como separador decimal. El módulo csv de la biblioteca estándar se ocupa de todos los detalles:
import csv
agenda = [
{"titulo": "Cartel feria", "responsable": "Marta", "prioridad": "alta", "dias": 3},
{"titulo": "Menu, con tapas", "responsable": "Luis", "prioridad": "media", "dias": 5}]
with open("tareas.csv", "w", encoding="utf-8", newline="") as f:
escritor = csv.DictWriter(f, fieldnames=["titulo", "responsable", "prioridad", "dias"])
escritor.writeheader() # escribe la fila de cabecera
escritor.writerows(agenda) # una fila por diccionario
with open("tareas.csv", "r", encoding="utf-8", newline="") as f:
for fila in csv.DictReader(f): # cada fila es un diccionario
print(fila["titulo"], fila["dias"], type(fila["dias"]))Cuatro cosas que conviene entender de este código:
DictWriteryDictReadertrabajan con diccionarios, que es justo lo que tiene nuestra agenda. Sus primoscsv.writerycsv.readerhacen lo mismo con listas, y son más simples cuando no hay cabecera.fieldnamesfija el orden de las columnas y debe coincidir con las claves de los diccionarios. Ynewline=""en elopenes un detalle técnico obligatorio con el módulocsv: sin él, en Windows aparecen líneas en blanco entre fila y fila.- Todo lo que se lee de un CSV es texto.
fila["dias"]vale"3", no3; si necesitas el número,int(fila["dias"]). Es la misma norma que coninput()en 02-03.
Y el problema de las comas dentro de un campo queda resuelto: el título "Menu, con tapas" se escribe entrecomillado automáticamente y se vuelve a leer entero. Eso es precisamente lo que hace el módulo csv y lo que no hace un split(",") escrito a mano.
- JSON: estructuras completas
JSON (JavaScript Object Notation) es un formato de texto pensado para representar estructuras anidadas: listas dentro de diccionarios dentro de listas, con sus tipos conservados. Es el formato en que se comunican prácticamente todas las aplicaciones de internet, y encaja como un guante con lo que aprendiste en 05-04.
| Python | JSON |
|---|---|
dict |
objeto { } |
list, tuple |
array [ ] |
str |
string (siempre con comillas dobles) |
int, float |
number |
True / False |
true / false |
None |
null |
Ojo a dos detalles: una tupla se guarda como array y vuelve convertida en lista, y los conjuntos (set) no tienen equivalente, así que hay que convertirlos a lista antes de guardar. El módulo json ofrece cuatro funciones que se distinguen por una s de string:
| Función | Qué hace |
|---|---|
json.dump(dato, fichero) |
Escribe la estructura en un fichero |
json.load(fichero) |
Lee un fichero y devuelve la estructura |
json.dumps(dato) |
Devuelve la estructura como cadena |
json.loads(cadena) |
Convierte una cadena en estructura |
import json
agenda = [{"titulo": "Cartel feria", "responsable": "Marta", "dias": 3,
"completada": False}]
with open("tareas.json", "w", encoding="utf-8") as f:
json.dump(agenda, f, indent=2, ensure_ascii=False)
with open("tareas.json", "r", encoding="utf-8") as f:
recuperada = json.load(f)
print(recuperada == agenda) # True -> la estructura vuelve intacta
print(type(recuperada[0]["dias"])) # <class 'int'> -> los tipos se conservanLos dos parámetros opcionales son importantes en la práctica. indent=2 escribe el fichero con saltos de línea y sangrado, de modo que una persona pueda leerlo y corregirlo con un editor; sin él sale todo en una línea. Y ensure_ascii=False hace que las tildes y la ñ se escriban tal cual: por defecto, json.dump las convierte en secuencias como \u00f1, que son válidas pero ilegibles.
La gran ventaja sobre el CSV se ve en la última línea: JSON conserva los tipos. Los enteros vuelven como enteros y los booleanos como booleanos, sin conversiones manuales. Por eso será el formato principal de TareaFácil.
- Texto, CSV o JSON: cuál usar
| Texto plano | CSV | JSON | |
|---|---|---|---|
| Estructura | Ninguna | Tabla de filas y columnas | Anidada, a cualquier nivel |
| ¿Conserva los tipos? | No | No: todo es texto | Sí |
| ¿Lo abre una hoja de cálculo? | No | Sí | No |
| Módulo necesario | Ninguno | csv |
json |
| Ideal para | Registros, notas, logs | Entregar datos a alguien | Guardar el estado del programa |
En resumen: texto cuando solo hay líneas sueltas y no vas a volver a interpretarlas; CSV cuando los datos son una tabla y alguien va a abrirlos con una hoja de cálculo; JSON cuando quieres guardar y recuperar una estructura de Python tal cual. Nada impide combinarlos, que es justo lo que haremos: JSON como almacén de trabajo y CSV como exportación para Marta.
- Comprobar si el fichero existe
La primera vez que se ejecuta el programa no hay nada guardado, y abrir en modo "r" un fichero inexistente lanza FileNotFoundError. La comprobación previa es sencilla:
ruta = Path("tareas.json")
if ruta.exists():
with open(ruta, "r", encoding="utf-8") as f:
agenda = json.load(f)
else:
agenda = [] # primera ejecucion: empezamos con la agenda vaciaEste patrón —comprobar y decidir— es suficiente para nuestro programa, aunque tiene dos límites: no cubre que el fichero exista pero esté corrupto (un JSON a medio escribir) ni que exista sin permiso de lectura. La solución robusta es intentar la operación y capturar el error si ocurre, con try/except, que se estudia en Depuración y manejo de errores. Por ahora, Path.exists() nos vale.
- TareaFácil v0.11: la agenda sobrevive
Con tres funciones nuevas, el programa deja de olvidar. cargar_tareas se llama al arrancar, guardar_tareas al salir, y exportar_csv cuando Marta quiere el listado en una hoja de cálculo. El menú pasa a siete opciones.
# tareafacil.py - Estudio Alba / Version 0.11: la agenda se guarda en disco
import csv, json
from pathlib import Path
RUTA_JSON = Path("tareas.json")
RUTA_CSV = Path("tareas.csv")
CAMPOS = ("titulo", "responsable", "prioridad", "dias", "completada")
OPCIONES = ("1", "2", "3", "4", "5", "6", "7")
# --- Resto de constantes y funciones: sin cambios respecto a la v0.10 ---
def guardar_tareas(agenda, ruta=RUTA_JSON):
"""Guarda la agenda completa en un fichero JSON."""
with open(ruta, "w", encoding="utf-8") as fichero:
json.dump(agenda, fichero, indent=2, ensure_ascii=False)
print(f"Agenda guardada en {ruta} ({len(agenda)} tareas).")
def cargar_tareas(ruta=RUTA_JSON):
"""Devuelve la agenda guardada, o una lista vacia si no hay fichero."""
if not ruta.exists():
print("No hay agenda previa: empezamos de cero.")
return []
with open(ruta, "r", encoding="utf-8") as fichero:
agenda = json.load(fichero)
print(f"Agenda cargada desde {ruta}: {len(agenda)} tareas.")
return agenda
def exportar_csv(agenda, ruta=RUTA_CSV):
"""Exporta la agenda a un CSV que Marta pueda abrir en la hoja de calculo."""
if not agenda:
print("No hay nada que exportar.")
return
with open(ruta, "w", encoding="utf-8", newline="") as fichero:
escritor = csv.DictWriter(fichero, fieldnames=CAMPOS, delimiter=";")
escritor.writeheader()
escritor.writerows(agenda)
print(f"Exportadas {len(agenda)} tareas a {ruta}.")
def main():
"""Ejecuta el bucle principal de la aplicacion."""
agenda = cargar_tareas()
while True:
mostrar_menu()
opcion = pedir_opcion("Elige una opcion (1-7): ", OPCIONES)
if opcion == "7":
if confirmar("Seguro que quieres salir?"):
guardar_tareas(agenda)
print("Hasta luego. TareaFacil se cierra.")
break
elif opcion == "1":
registrar_tarea(agenda)
elif opcion == "2":
mostrar_listado(agenda)
elif opcion == "3":
tarea = elegir_tarea(agenda)
if tarea is not None:
cambiar_prioridad(tarea)
elif opcion == "4":
tarea = elegir_tarea(agenda)
if tarea is not None:
marcar_completada(tarea)
elif opcion == "5":
resumen_por_responsable(agenda)
elif opcion == "6":
exportar_csv(agenda)
pausar()
if __name__ == "__main__":
main()Los detalles que hacen que esto funcione:
cargar_tareasdevuelve[]cuando no hay fichero, así que la primera ejecución no falla y el resto del programa no necesita saber nada del disco: recibe una lista de diccionarios, como siempre.guardar_tareasse llama justo antes delbreak, dentro de la confirmación de salida. Es el momento exacto en que la agenda está completa.- Las rutas son parámetros con valor por defecto (04-02): el programa usa
tareas.jsonsin decir nada, pero cualquiera puede llamar aguardar_tareas(agenda, Path("copia.json"))para hacer una copia de seguridad. CAMPOSes una tupla —constante, no debe cambiar (05-04)— y fija el orden de las columnas del CSV, que se escribe condelimiter=";"para que la hoja de cálculo española reparta bien las columnas.
Quedan dos limitaciones honestas. Si el programa se cierra de forma anormal —un corte de luz, un Ctrl+C— los cambios se pierden, porque solo se guarda al salir; la solución es llamar a guardar_tareas después de cada modificación, una línea por opción de menú. Y si el JSON está corrupto, json.load lanzará un error que todavía no sabemos capturar. Ambas cosas se arreglan en el módulo 8.
Errores Comunes y Consejos
Abrir en modo "w" lo que querías leer. El fichero se vacía en ese instante y no hay marcha atrás. Antes de escribir cualquier open(..., "w"), comprueba que esa es de verdad tu intención.
Olvidar el "\n" al escribir, con lo que el fichero acaba siendo una sola línea interminable, o olvidar el .rstrip() al leer, con lo que las comparaciones fallan por un carácter invisible pegado al final.
No indicar encoding="utf-8". Funciona en tu ordenador y se rompe en el de al lado, con tildes convertidas en símbolos raros. Ponlo siempre.
Esperar que el CSV devuelva números. Todo lo que sale de un CSV es texto: convierte con int() o float(). Y guardar un conjunto en JSON lanza TypeError: Object of type set is not JSON serializable; conviértelo antes con list(mi_conjunto).
Consejo: guarda en un fichero temporal y renombra. Para datos importantes, escribe primero en tareas.tmp y renombra al terminar; así, si el programa se interrumpe a mitad, el fichero bueno sigue intacto.
Consejo: separa la lógica de la persistencia. guardar_tareas y cargar_tareas son las únicas funciones que tocan el disco; el resto del programa trabaja con listas y diccionarios sin enterarse. Es el mismo principio de 04-04 —separar entrada y salida de la lógica— aplicado a los ficheros.
Ejercicios
Ejercicio 1: Predecir el comportamiento
Explica qué hace cada bloque y qué contendrá notas.txt al final. Indica también qué error se produciría si notas.txt no existiera al llegar al tercer bloque.
with open("notas.txt", "w", encoding="utf-8") as f:
f.write("Cartel feria\n")
f.write("Menu Sole\n")
with open("notas.txt", "w", encoding="utf-8") as f:
f.write("Logotipo Vidal\n")
with open("notas.txt", "a", encoding="utf-8") as f:
f.write("Flyer verano")
with open("notas.txt", "r", encoding="utf-8") as f:
for i, linea in enumerate(f, start=1):
print(i, repr(linea))Ejercicio 2: De CSV a JSON
Escribe csv_a_json(ruta_csv, ruta_json), que lea un CSV con cabecera titulo;responsable;dias;completada separado por punto y coma, convierta dias a entero y completada al booleano correspondiente (el texto "True" es True, cualquier otro es False), y guarde el resultado como JSON legible. Si el CSV no existe, debe avisar y no hacer nada.
Ejercicio 3: Copia de seguridad con fecha
Escribe hacer_copia(agenda, etiqueta) que guarde la agenda en un fichero llamado copia-<etiqueta>.json dentro de una carpeta copias, creándola si no existe (Path.mkdir(exist_ok=True)), y devuelva la ruta usada. Después, escribe listar_copias() que imprima los nombres de todos los ficheros .json de esa carpeta usando Path.glob("*.json").
Soluciones
Solución 1.
El primer bloque crea el fichero con dos líneas. El segundo lo abre otra vez en modo "w", lo que borra todo antes de escribir: las dos primeras líneas desaparecen. El tercero, en modo "a", añade al final sin borrar, pero sin "\n", así que la última línea queda sin salto final. Al leer con repr() se ven los \n explícitos, que es la forma de comprobar qué hay realmente en cada línea. Si el fichero no existiera al llegar al tercer bloque, no pasaría nada: el modo "a" lo crea. El error FileNotFoundError solo aparecería en el cuarto bloque, que abre en modo "r".
Solución 2.
def csv_a_json(ruta_csv, ruta_json):
"""Convierte un CSV de tareas en un JSON con los tipos correctos."""
ruta_csv = Path(ruta_csv)
if not ruta_csv.exists():
print(f"No existe el fichero {ruta_csv}.")
return []
agenda = []
with open(ruta_csv, "r", encoding="utf-8", newline="") as f:
for fila in csv.DictReader(f, delimiter=";"):
agenda.append({
"titulo": fila["titulo"].strip(),
"responsable": fila["responsable"].strip().capitalize(),
"dias": int(fila["dias"]),
"completada": fila["completada"].strip() == "True"})
with open(ruta_json, "w", encoding="utf-8") as f:
json.dump(agenda, f, indent=2, ensure_ascii=False)
print(f"Convertidas {len(agenda)} tareas.")
return agendaLa conversión de tipos es el meollo del ejercicio, y es donde se ve la diferencia entre los dos formatos: del CSV sale "3" y "True" como texto, y hay que traducirlos a 3 y True a mano. La expresión fila["completada"].strip() == "True" es el booleano: una comparación ya devuelve True o False, así que no hace falta ningún if. Fíjate además en que la guarda con return [] va al principio, siguiendo el patrón de 04-02.
Solución 3.
def hacer_copia(agenda, etiqueta):
"""Guarda la agenda en copias/copia-<etiqueta>.json y devuelve la ruta."""
carpeta = Path("copias")
carpeta.mkdir(exist_ok=True) # no falla si ya existe
destino = carpeta / f"copia-{etiqueta}.json"
with open(destino, "w", encoding="utf-8") as f:
json.dump(agenda, f, indent=2, ensure_ascii=False)
return destino
def listar_copias():
"""Imprime las copias disponibles, de la mas reciente a la mas antigua."""
carpeta = Path("copias")
if not carpeta.exists():
print("Todavia no hay ninguna copia.")
return
for ruta in sorted(carpeta.glob("*.json"), reverse=True):
print(f"{ruta.name:<28}{ruta.stat().st_size:>8} bytes")
hacer_copia(agenda, "2026-08-04")
listar_copias() # copia-2026-08-04.json 412 bytescarpeta / f"copia-{etiqueta}.json" combina el operador de rutas de pathlib con una f-string, que es la forma limpia de construir nombres de fichero variables. mkdir(exist_ok=True) evita tener que comprobar antes si la carpeta existe. Y glob("*.json") devuelve todas las rutas que encajan con el patrón; al ordenarlas al revés y nombrar las copias con la fecha en formato aaaa-mm-dd, salen automáticamente de la más reciente a la más antigua.
Conclusión del módulo
La persistencia es el puente entre la memoria, que se borra, y el disco, que no. Se cruza con open() dentro de un bloque with —que garantiza el cierre— indicando el modo (r para leer, w para escribir sabiendo que vacía el fichero, a para añadir, x para crear en exclusiva) y siempre encoding="utf-8". Sobre esa base, tres formatos: texto plano, que se escribe con write (recordando el "\n") y se lee línea a línea con for linea in f (recordando el .rstrip()); CSV, la tabla que abre cualquier hoja de cálculo, gestionada con csv.DictWriter y csv.DictReader, que resuelven la cabecera, el separador y las comas dentro de un campo, pero devuelven todo como texto; y JSON, que guarda estructuras anidadas completas con json.dump/json.load —indent para que se lea, ensure_ascii=False para las tildes— y es el único que conserva los tipos. Y antes de leer, Path.exists(), a la espera del try/except de 08-02.
Con esto se cierra el módulo 5 y quedan asentadas las cinco estructuras que necesitarás a diario: la lista para muchas cosas del mismo tipo, con sus índices, rebanadas, métodos y comprensiones; la cadena como secuencia inmutable con su catálogo de métodos y el puente split/join; el diccionario para dar nombre a cada campo; el conjunto para valores únicos y pertenencia; y la tupla para registros fijos y valores devueltos. Combinándolas nace la estructura que sostiene casi cualquier programa real: la lista de diccionarios.
TareaFácil ha recorrido en cinco lecciones el camino que va de un programa de juguete a una herramienta: de una tarea en seis variables sueltas (v0.7) a listas paralelas frágiles (v0.8), de ahí a la tarea como diccionario (v0.9), a la agenda como lista de diccionarios con todo el menú operativo (v0.10) y, por fin, a una v0.11 que recuerda. Marta puede cerrar el programa, irse a casa y encontrarse su agenda intacta al día siguiente, además de exportarla a CSV cuando necesita enseñar el reparto de trabajo.
Y ese éxito trae el siguiente problema, que es el mejor tipo de problema. Cuando la agenda tenga doscientas tareas guardadas, «buscar la del cliente Vidal» dejará de ser trivial y «ordenar por prioridad» dejará de ser gratis. En el módulo 6 abriremos las herramientas que hemos estado usando como cajas negras: Algoritmos de búsqueda explicará cómo se localiza un dato y por qué buscar en un conjunto es tan rápido, Algoritmos de ordenamiento mostrará qué hace sorted por dentro, y Eficiencia y notación Big-O te dará por fin el vocabulario para decir cuánto cuesta cada cosa.
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
