Archivos, errores y módulos

Python · Lección 4

L3 From scratch Fase F0 Libro McKinney cap. 3 Prereqs Comprehensions

La idea

Tres cosas que separan un script de notebook de código que otra persona puede correr: cerrar lo que abres, fallar con un mensaje útil, y poder importarse sin ejecutarse.

Archivos: siempre con with

with garantiza que el archivo se cierra aunque el bloque lance una excepción. Sin with, un error a media lectura deja el descriptor abierto; con miles de archivos, el proceso se queda sin descriptores y muere con un error que no dice nada.

Importantencoding="utf-8" siempre

El encoding por defecto depende del sistema operativo. Tu código funcionará en tu Mac y fallará en el servidor de la empresa con UnicodeDecodeError en la primera tilde. Escríbelo explícito, siempre.

Leer línea por línea, sin cargar todo (Lección 3: el objeto archivo es un generador):

Para CSV real usa el módulo csv o pandas — manejan comas dentro de comillas, saltos de línea en campos y encodings raros. Lo de arriba es para entender qué hacen por dentro.

Rutas: pathlib, no concatenar strings

Path resuelve solo la diferencia entre / de macOS y \ de Windows. Concatenar strings con + o con os.path.join funciona, pero pathlib se lee mejor y trae .exists(), .mkdir(parents=True), .glob() y .read_text() incluidos.

Errores: capturar lo específico

WarningNunca except: pelado
try:
    hacer_algo()
except:          # ← captura TODO, incluido Ctrl-C y errores de memoria
    pass         # ← y lo tira a la basura en silencio

Eso convierte un bug en un misterio. Captura la excepción específica que sabes que puede ocurrir, y si de verdad necesitas capturar amplio, usa except Exception as e: y registra e.

Lanzar tus propios errores, con mensaje útil:

Un mensaje que incluye el valor que causó el problema te ahorra media hora de debugging. "trx inválido" no sirve; "trx debe ser positivo, recibí 0" sí.

Módulos y el guardián __main__

Cuando escribes mi_script.py y alguien hace import mi_script, todo el archivo se ejecuta. Si tenía código suelto al final, ese código corre en el import — casi nunca es lo que quieres.

__name__ vale "__main__" cuando el archivo se ejecuta directamente, y el nombre del módulo cuando se importa. Ese if es la línea que separa un script de una librería reutilizable — y es lo que vas a usar en cada archivo de scratch/.

Ejercicio 1 — Leer con manejo de error

Completa la función para que devuelva el contenido del archivo, o la cadena "no existe" si el archivo no está, sin dejar que la excepción se propague.

La excepción que lanza open() cuando el archivo no está es FileNotFoundError. Para leer, usa with open(ruta, encoding="utf-8") as f: return f.read().

Ejercicio 2 — Validar con raise

Escribe la validación: si alguna venta es negativa, lanza ValueError con un mensaje que incluya el valor ofensor.

La condición es v < 0. El mensaje debe contener el número: usa un f-string, por ejemplo f"venta negativa: {v}".

Reto

En proyectos/notebooks/F0-retos.ipynb:

  1. Escribe cargar_ventas(ruta) que lea un CSV con csv.DictReader, convierta venta a float y trx a int, y lance ValueError con el número de línea si alguna fila tiene datos inválidos.
  2. Crea scratch/io_utils.py con esa función y su bloque if __name__ == "__main__": de prueba. Impórtala desde un notebook y confirma que el bloque de prueba no corre.
  3. Escribe un context manager propio con contextlib.contextmanager que cronometre el bloque: with cronometro("carga"): ... debe imprimir cuánto tardó.

Para el Cerebro

Lee McKinney cap. 3 (archivos y excepciones) con estas preguntas escritas antes:

  1. ¿Qué diferencia hay entre abrir un archivo en modo "w", "a" y "x"?
  2. ¿Qué es un context manager y qué dos métodos debe implementar?
  3. ¿Qué hace else en un try/except/else/finally y cuándo sirve?
¿Qué garantiza `with` al abrir un archivo?::Que se cierra aunque el bloque lance una excepción
¿Por qué escribir encoding="utf-8" explícito?::El default depende del SO; sin él, el código rompe en otra máquina
¿Por qué nunca usar `except:` pelado?::Captura todo, incluido Ctrl-C, y esconde bugs
¿Qué debe incluir un buen mensaje de error?::El valor concreto que causó el problema
¿Qué vale __name__ al ejecutar un archivo directamente?::"__main__"; al importarlo vale el nombre del módulo
¿Por qué pathlib y no concatenar strings?::Resuelve separadores por SO y trae exists, mkdir, glob y read_text

Fuentes

Lo que esta página demuestra sola. Los ejercicios corren en tu navegador.

Lo que viene de los libros.

Índices verificados el 11-09-2026 contra el índice publicado de cada libro.

→ Siguiente: Entorno: uv, git y el repo