26 · try / except / else / finally y excepciones personalizadas
Captura errores con elegancia, entiende la jerarquía de excepciones, define clases de excepción personalizadas y aprende por qué la cultura Python prefiere EAFP sobre LBYL.
Python usa **excepciones** para toda señalización de errores. `try` envuelve código riesgoso; `except SomeError as e` captura errores específicos; `else` se ejecuta solo cuando no ocurrió ninguna excepción; `finally` siempre se ejecuta — incluso después de un `return` u otra excepción. Nunca uses `except:` a secas — también captura `KeyboardInterrupt` y `SystemExit`.
Sin esto:
Sin manejo de excepciones, cualquier entrada inesperada — un batch corrupto, un archivo faltante, un string no numérico — tumba todo el programa. Con `try/except`, puedes registrar el fallo, saltar el elemento malo y mantener el bucle de entrenamiento en marcha.
Cuando Python encuentra un error en tiempo de ejecución, lanza una excepción — un objeto que viaja por la pila de llamadas hasta que algo la captura o el programa termina. La jerarquía de clases comienza en BaseException (que incluye KeyboardInterrupt y SystemExit) y se ramifica en Exception (la base para todos los errores regulares) — luego ValueError, TypeError, KeyError, FileNotFoundError, ZeroDivisionError y cientos más. El constructo try/except/else/finally permite manejar errores con elegancia: ejecuta código riesgoso en try, captura tipos de error específicos en except, ejecuta código solo en caso de éxito en else y ejecuta limpieza en finally sin importar el resultado. También puedes definir tus propias clases de excepción haciendo subclase de Exception — así es como las bibliotecas de ML señalan fallos específicos del dominio como errores de convergencia o modelos no ajustados.
Python (in browser)
`except ValueError as e` vincula el objeto de excepción a `e` — puedes inspeccionar `str(e)` para un mensaje legible y `type(e).__name__` para el nombre de la clase. `except (TypeError, ZeroDivisionError) as e` captura cualquiera de los dos tipos en una sola cláusula.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
Ejecuta esto y lee el resultado con atención. `else` solo se activa en el camino exitoso — si `try` lanza cualquier cosa, `else` se omite por completo. `finally` se activa en todos los caminos: éxito, coincidencia con `except`, excepción no coincidente e incluso `return`. Esto hace de `finally` el lugar correcto para liberar recursos (conexiones de base de datos, locks, archivos temporales).
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
`class ConvergenceError(Exception): pass` es todo lo que necesitas para una excepción personalizada significativa. Añadir un docstring explica cuándo debe lanzarse. Los llamadores la capturan con `except ConvergenceError as e` — pueden distinguirla de un `ValueError` genérico sin analizar strings de mensajes.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
EAFP es idiomático para contenedores porque el `try/except` de CPython tiene costo casi cero en el camino exitoso — el mecanismo de excepciones solo se activa en el fallo. Para comprobaciones costosas (stat de archivo, sondeo de red) LBYL puede ser más legible, pero `.get()` / `.setdefault()` / `.pop(key, default)` son las verdaderas alternativas de una línea para acceso a dicts.
La lección mml-39 cubre la convergencia del descenso de gradiente. Cuando la regla de actualización falla en converger — pérdida explosiva, gradientes NaN — la señal correcta es un `ConvergenceError` o una bandera de parada anticipada, no una respuesta silenciosamente errónea. El diseño de excepciones y los criterios de convergencia son dos caras de la misma decisión de ingeniería.
¿Se ejecuta el bloque `finally` si se ejecuta un `return` dentro de `try`?
- `try/except SomeError as e` captura errores específicos. `else` se ejecuta solo en caso de éxito. `finally` siempre se ejecuta — perfecto para limpieza. Nunca uses `except:` a secas.
- Define excepciones personalizadas con `class MyError(Exception): pass`. Lanza con `raise MyError("mensaje")`. Encadena con `raise NewError("contexto") from original_exc`.
- La cultura Python prefiere EAFP (`try/except`) sobre LBYL (`if key in d`). Para valores por defecto en dicts, `d.get(key, default)` es el idiom de una línea.
Los pipelines de ML lanzan excepciones personalizadas a lo largo de su ejecución: `ConvergenceError` cuando el descenso de gradiente diverge; un `DataError` envuelto al leer un batch corrupto; `torch.cuda.OutOfMemoryError` de PyTorch cuando un batch es demasiado grande para la memoria GPU; `NotFittedError` de scikit-learn cuando se llama `.predict()` antes que `.fit()`. El patrón `try/finally` también protege la memoria GPU: `try: train_step()` seguido de `finally: torch.cuda.empty_cache()` asegura que el caché se limpia incluso si el paso lanza una excepción.
Si lo quitas: Sin manejo de excepciones, un único archivo corrupto o una pérdida NaN en una ejecución de entrenamiento de 72 horas tumba todo el trabajo. El manejo de excepciones es la diferencia entre un script frágil y un pipeline de calidad de producción que registra errores, salta batches malos y sigue ejecutando.