Lección 8 de 12 · Nivel 2: Intermedio
Archivos, manejo de errores y proyecto final en Python
En esta lección
- Manejar errores con try/except.
- Leer y escribir archivos de texto, CSV y JSON.
- Construir un gestor de gastos completo.
- Cerrar el nivel intermedio con un proyecto real.
Pon el archivo en la misma carpeta que tu programa para probar la lectura del CSV y el resumen por categoría.
- gastos.csv · 40 gastos de ejemplo para el gestor de gastos
En esta lección, que cierra el nivel intermedio, tus programas dan el salto a lo útil de verdad: guardar y leer datos en archivos para no perder nada al cerrarlos, y manejar errores para que no se detengan cuando algo sale mal. Al final construirás un proyecto completo: un gestor de gastos personales que guarda la información en un archivo CSV que puedes abrir en Excel.
Manejo de errores con try / except
Cuando algo falla (el usuario escribe letras donde esperabas un número, un archivo no existe, divides entre cero), Python lanza una excepción y el programa se detiene. Con try/except la capturas y decides qué hacer:
try:
edad = int(input("Edad: "))
print(f"El año que viene tendrás {edad + 1}")
except ValueError:
print("Eso no es un número entero")
Las excepciones más comunes
| Excepción | Cuándo ocurre |
|---|---|
ValueError | Conversión imposible: int("hola") |
ZeroDivisionError | División entre cero |
KeyError | Clave inexistente en un diccionario |
IndexError | Posición inexistente en una lista |
FileNotFoundError | El archivo no existe |
TypeError | Operación con tipos incompatibles: "5" + 3 |
Estructura completa
try:
a = float(input("Dividendo: "))
b = float(input("Divisor: "))
resultado = a / b
except ValueError:
print("Escribe números válidos")
except ZeroDivisionError:
print("No se puede dividir entre cero")
else:
print(f"Resultado: {resultado:.2f}") # solo si no hubo error
finally:
print("Operación terminada") # siempre se ejecuta
Pedir un número hasta que sea válido
Una función que reutilizarás en casi todos tus programas:
def pedir_float(mensaje):
while True:
texto = input(mensaje).strip().replace(",", ".")
try:
return float(texto)
except ValueError:
print("Valor no válido, intenta de nuevo.")
Evita except: o except Exception: sin más, porque esconden errores de programación que deberías ver. Captura las excepciones concretas que esperas.
1. ¿Qué excepción lanza int("hola")?
ValueError aparece cuando el tipo es correcto (un texto) pero su contenido no se puede convertir.
Leer y escribir archivos de texto
La forma correcta de trabajar con archivos es con with, que los cierra automáticamente aunque haya un error:
# Escribir (crea el archivo o lo sobrescribe)
with open("notas.txt", "w", encoding="utf-8") as f:
f.write("Primera línea\n")
f.write("Segunda línea\n")
# Añadir al final sin borrar lo anterior
with open("notas.txt", "a", encoding="utf-8") as f:
f.write("Tercera línea\n")
# Leer línea por línea
with open("notas.txt", "r", encoding="utf-8") as f:
for linea in f:
print(linea.strip())
| Modo | Significado |
|---|---|
"r" | Leer (por defecto). Error si no existe. |
"w" | Escribir. Crea el archivo o borra su contenido. |
"a" | Añadir al final. |
Indica siempre encoding="utf-8": evita problemas con tildes y eñes, sobre todo en Windows.
2. Abres con modo "w" un archivo que ya existe. ¿Qué pasa?
"w" sobrescribe; para añadir al final sin borrar, usa "a".
Archivos CSV: datos en forma de tabla
CSV es el formato universal para intercambiar tablas: lo abren Excel, Google Sheets y cualquier base de datos. Python trae el módulo csv:
import csv
gastos = [
{"fecha": "2026-10-01", "categoria": "comida", "monto": 35.5},
{"fecha": "2026-10-02", "categoria": "transporte", "monto": 12.0},
]
# Escribir
with open("gastos.csv", "w", newline="", encoding="utf-8") as f:
escritor = csv.DictWriter(f, fieldnames=["fecha", "categoria", "monto"])
escritor.writeheader()
escritor.writerows(gastos)
# Leer
with open("gastos.csv", newline="", encoding="utf-8") as f:
for fila in csv.DictReader(f):
print(fila["categoria"], float(fila["monto"]))
Al leer un CSV, todos los valores llegan como texto: convierte los números con float(). Si al abrir el archivo en Excel ves todo en una columna o las tildes raras, usa Datos → Desde texto/CSV y elige UTF-8 y coma como separador (o escribe el archivo con delimiter=";" y encoding="utf-8-sig" si tu Excel usa punto y coma).
Archivos JSON: datos con estructura
JSON guarda diccionarios y listas tal cual. Es el formato de las APIs web y de muchos archivos de configuración:
import json
agenda = {"Ana": "0991234567", "Luis": "0987654321"}
with open("agenda.json", "w", encoding="utf-8") as f:
json.dump(agenda, f, ensure_ascii=False, indent=2)
with open("agenda.json", encoding="utf-8") as f:
agenda = json.load(f)
Con esto puedes resolver el problema de la agenda de la lección 6: cárgala al iniciar y guárdala al salir.
3. Quieres guardar un diccionario con listas dentro y recuperarlo tal cual. ¿Qué usas?
JSON conserva la estructura de diccionarios y listas; CSV es para tablas planas.
Proyecto del nivel intermedio: gestor de gastos personales
Este programa reúne todo el curso: variables, condicionales, bucles, diccionarios, funciones, archivos y manejo de errores. Permite registrar gastos, ver un resumen por categoría y los guarda en gastos.csv.
import csv
from datetime import date
from pathlib import Path
ARCHIVO = Path("gastos.csv")
CAMPOS = ["fecha", "categoria", "descripcion", "monto"]
def cargar_gastos():
"""Lee el CSV y devuelve una lista de diccionarios."""
if not ARCHIVO.exists():
return []
with open(ARCHIVO, newline="", encoding="utf-8") as f:
gastos = list(csv.DictReader(f))
for g in gastos:
g["monto"] = float(g["monto"])
return gastos
def guardar_gasto(gasto):
"""Añade un gasto al final del CSV (crea el archivo si no existe)."""
nuevo = not ARCHIVO.exists()
with open(ARCHIVO, "a", newline="", encoding="utf-8") as f:
escritor = csv.DictWriter(f, fieldnames=CAMPOS)
if nuevo:
escritor.writeheader()
escritor.writerow(gasto)
def pedir_float(mensaje):
while True:
try:
valor = float(input(mensaje).strip().replace(",", "."))
if valor > 0:
return valor
print("El monto debe ser mayor que 0.")
except ValueError:
print("Escribe un número válido.")
def registrar():
categoria = input("Categoría (comida, transporte, ocio...): ").strip().lower() or "otros"
descripcion = input("Descripción: ").strip()
monto = pedir_float("Monto: ")
gasto = {"fecha": date.today().isoformat(), "categoria": categoria,
"descripcion": descripcion, "monto": monto}
guardar_gasto(gasto)
print("✓ Gasto registrado")
def resumen(gastos):
if not gastos:
print("Todavía no hay gastos.")
return
total = sum(g["monto"] for g in gastos)
por_categoria = {}
for g in gastos:
por_categoria[g["categoria"]] = por_categoria.get(g["categoria"], 0) + g["monto"]
print(f"\n{'Categoría':15}{'Monto':>10}{'%':>8}")
print("-" * 33)
for cat, monto in sorted(por_categoria.items(), key=lambda x: x[1], reverse=True):
print(f"{cat:15}{monto:10.2f}{monto / total:8.1%}")
print("-" * 33)
print(f"{'TOTAL':15}{total:10.2f}")
mayor = max(gastos, key=lambda g: g["monto"])
print(f"\nGasto más alto: {mayor['descripcion']} ({mayor['monto']:.2f})")
def main():
while True:
opcion = input("\n1) Registrar gasto 2) Ver resumen 3) Salir: ").strip()
if opcion == "1":
registrar()
elif opcion == "2":
resumen(cargar_gastos())
elif opcion == "3":
print("¡Hasta pronto!")
break
else:
print("Opción no válida")
if __name__ == "__main__":
main()
Lo que acabas de usar
pathlib.Pathpara comprobar si el archivo existe.or "otros": si el usuario deja la categoría vacía, se usa un valor por defecto (el texto vacío es "falso").- Formato de columnas con f-strings:
{cat:15}reserva 15 caracteres;{monto:>10}alinea a la derecha;{x:8.1%}muestra porcentaje. sorted(..., key=..., reverse=True)para ordenar las categorías de mayor a menor gasto.
Retos para mejorar el proyecto
- Añade una opción para ver solo los gastos del mes actual (pista: compara
g["fecha"][:7]condate.today().isoformat()[:7]). - Define un presupuesto por categoría en un diccionario y avisa cuando una categoría lo supere.
- Agrega una opción para eliminar el último gasto registrado (tendrás que reescribir el archivo completo).
- Instala pandas (
pip install pandas) y genera el resumen conpd.read_csv("gastos.csv").groupby("categoria")["monto"].sum().
Solución del reto 1
def gastos_del_mes(gastos):
mes_actual = date.today().isoformat()[:7] # "2026-10"
return [g for g in gastos if g["fecha"][:7] == mes_actual]
# En el menú:
# elif opcion == "4":
# resumen(gastos_del_mes(cargar_gastos()))
Resumen
try/exceptcaptura errores concretos;elseyfinallycompletan la estructura.- Abre archivos siempre con
withyencoding="utf-8". - CSV para tablas (módulo
csv), JSON para datos con estructura (módulojson). - El gestor de gastos combina todo lo que viste hasta ahora en un programa real.
¡Completaste el nivel intermedio! En la próxima lección empieza el nivel avanzado con la programación orientada a objetos: convertirás los diccionarios de tu gestor de gastos en clases.
Crea tu cuenta, marca tus avances y descarga el certificado al terminar el curso.