2.4 · Gestión de errores y excepciones

Nivel 2 · Estructuras de datos y POO

2.4Gestión de errores y excepciones

Objetivo: Gestionar try/except/finally y crear sus propias excepciones.
Tiempo estimado: 12 min · lectura autónoma · Si solo tienes 5 min: lee «Lo esencial en 30s» + el esquema.

Lo esencial en 30 segundos

  • Enmarcar lo arriesgado preciso: try sobre la conversión, el archivo, la red — nunca todo el programa.
  • Atrapar preciso, no amplio: except ValueError/FileNotFoundError dirigido — except desnudo o pass entierra los bugs.
  • Señalar pronto y fuerte: raise para sus invariantes, else/finally con criterio, registrar y luego degradar correctamente.

Minicaso hilado — Nabil fiabiliza la importación del mes

Último acto: la importación mensual de Nabil falla al primer imprevisto — exportación ausente, monto no numérico, total dividido por cero cuando no hay ventas. Los usuarios ven una traza roja. Lo fiabiliza: conversión de los montos en try/except ValueError con nueva petición, apertura en try/except FileNotFoundError con mensaje claro, división protegida contra ZeroDivisionError, montos negativos rechazados por raise desde la creación de la Venta (2.3). El resto sube con su traceback: nada se silencia. La importación del nivel 2 está entregada: datos, texto, objetos, errores dominados.

Epílogo del hilo 2.1-2.3: la importación de Nabil ya no falla al primer imprevisto.

1. La excepción no es una catástrofe

ValueError, KeyError, FileNotFoundError, ZeroDivisionError: objetos informativos, no insultos. try/except enmarca lo arriesgado preciso (conversión de una entrada, apertura de un archivo, división) — nunca todo el programa: atrapar amplio oculta los bugs. La traceback se lee de abajo arriba: el tipo de error y luego la línea culpable. Nabil lo aplica: solo la conversión de los montos está enmarcada, no toda la importación.

2. try/except/else/finally: cada uno su papel

try contiene lo arriesgado, except ValueError atrapa un tipo preciso con un mensaje útil, else ejecuta lo siguiente si todo va bien, finally limpia siempre (cerrar, liberar — incluso en caso de error). raise ValueError("...") señala sus propios invariantes: un monto negativo se rechaza pronto y fuerte, desde la creación. Nabil rechaza así los montos negativos en __init__: el error se señala en la fuente, no tres funciones más lejos.

3. Dejar pasar lo que no se trata

Regla de oro: solo se atrapa lo que se sabe tratar (volver a pedir, valor por defecto, mensaje claro). El resto sube con su traceback: silenciarlo con except: pass entierra los bugs vivos — el peor reflejo. Un except desnudo enmascara todo, incluso lo que debería ver. En producción, se registra y luego se degrada correctamente (mensaje al usuario, parada controlada). La importación de Nabil vuelve a pedir los montos inválidos, señala la exportación ausente — y deja subir lo inesperado.

Error frecuente de interpretación: «Para estar seguro, lo atrapo todo con except: y pongo pass». No, es la forma más segura de no ver nada: un except desnudo atrapa incluso los bugs de su propio código y pass los entierra — el programa continúa con datos falsos, sin ninguna señal. La regla de Nabil: except dirigido (ValueError, FileNotFoundError) + tratamiento real (volver a pedir, defecto, mensaje); lo que no se sabe tratar sube y se registra.
Errores dominados Enmarcar, tratar, señalar try (arriesgado) Conversión,archivo — bloque estrechoexcept (preciso) Tipodirigido + tratamiento realraise/log (señalar) Invariantespronto, inesperado que sube
Enmarcar, tratar, señalar
Versión en texto del esquema (SEO / lectores de pantalla / traducción): 1) Nabil enmarca lo ARRIESGADO PRECISO con try (conversión de los montos, apertura de la exportación) — nunca todo el programa. 2) Atrapa PRECISO (except ValueError/FileNotFoundError) con un tratamiento real: nueva petición, mensaje claro, else para lo siguiente, finally para limpiar. 3) SEÑALA pronto con raise (monto negativo rechazado desde __init__) y deja pasar lo inesperado con su traceback, registrado — nunca except: pass. Lección: fallar pronto y fuerte, silenciar nunca.

¿Lo sabía?

Nabil escribió un día «except: pass» para acallar un error — y su informe de Argel quedó a cero durante una semana sin avisar a nadie.
Verdadero: silenciar un error entierra el bug vivo — se atrapa preciso y se trata.

Un bug silenciado trabaja contra usted en silencio.

🔎 Ejemplo concreto — 🔎 Ejemplo concreto — La entrada robusta de Nabil, sin silencio

Lo preciso se recupera, el resto subiría, como en casa de Nabil:

def leer_monto(texto):
    try:
        monto = float(texto)
    except ValueError:
        raise ValueError(f"monto inválido: {texto!r}") from None
    if monto < 0:
        raise ValueError("monto negativo prohibido")
    return monto

try:
    print(leer_monto("1250.5"))
except ValueError as e:
    print(f"Error: {e}")

Lectura: ValueError dirigido, invariante señalado por raise, mensaje útil.

Lección: nunca except: pass — entierra los bugs vivos.

Vocabulario de la sección

Excepción
Error que ocurre en la ejecución (ZeroDivisionError, ValueError, FileNotFoundError… ) que interrumpe el programa si no se gestiona. Leída de abajo arriba en la traceback.

Ejemplo sencillo: La exportación ausente de Nabil lanza FileNotFoundError.

Lo que no es: un error de sintaxis que impide el arranque.

Reflejo: fallo en la ejecución = excepción para leer.

try / except
try contiene el código arriesgado (bloque estrecho); except atrapa un TIPO PRECISO y ejecuta un tratamiento real en lugar de fallar.

Ejemplo sencillo: Nabil rodea float(monto) con un except ValueError.

Lo que no es: un except desnudo que lo atrapa todo sin tratar.

Reflejo: arriesgado preciso = try + except dirigido.

else / finally
else: lo siguiente si no hay error; finally: ejecutado EN TODOS LOS CASOS, ideal para liberar un recurso (cerrar un archivo).

Ejemplo sencillo: Nabil muestra «importación terminada» en finally.

Lo que no es: una limpieza que solo corre en caso de éxito.

Reflejo: limpieza garantizada = finally.

raise
Lanza voluntariamente una excepción para señalar una situación anormal (invariante violado): fallar pronto y fuerte, en la fuente.

Ejemplo sencillo: La Venta de Nabil rechaza los montos negativos por raise.

Lo que no es: un retorno silencioso que deja pasar datos falsos.

Reflejo: invariante violado = raise inmediato.

Traceback
La traza de error mostrada por Python: leída de abajo arriba, indica el tipo de error y la línea culpable. Para registrar, no para ocultar.

Ejemplo sencillo: Nabil lee el final de la traceback para encontrar la conversión culpable.

Lo que no es: un mensaje ocultado por except: pass.

Reflejo: traceback = leer el final, corregir la línea.

En la práctica — En práctica — Fiabilizar un programa con try/except

  1. Pida un monto y conviértalo: rodee float() con un try/except ValueError que vuelva a pedir en caso de entrada no numérica.
  2. Gestione una media atrapando ZeroDivisionError (ninguna venta) con un mensaje claro en lugar de un fallo.
  3. Abra la exportación en try/except FileNotFoundError; añada un finally que muestre «importación terminada» en todos los casos.
  4. Escriba una función que lance (raise) ValueError para un monto negativo, y pruébela con try/except.
Anticipa los errores con try/except/finally y lanza sus propias excepciones: sus programas se vuelven robustos ante lo imprevisto.

Script listo para decir — justificar la precisión en revisión de código (30 s)

«Para la importación de Nabil, cada try es estrecho: conversión en ValueError, exportación en FileNotFoundError, con un tratamiento real detrás. Los montos negativos van en raise desde la creación. Y nada de except pass: lo que no se sabe tratar sube y se registra — nunca se entierra un bug.»

Para decir mostrando un except desnudo sustituido por dos except dirigidos.

Modelo copiar-pegar — importación fiabilizada de principio a fin

import json
from pathlib import Path

def leer_monto(texto):
    monto = float(texto)
    if monto < 0:
        raise ValueError("monto negativo prohibido")
    return monto

fichero = Path("ventes.json")
try:
    lineas = json.loads(fichero.read_text(encoding="utf-8"))
except FileNotFoundError:
    print("Exportación no encontrada: verifique ventes.json")
    lineas = []
try:
    montos = [leer_monto(str(l)) for l in lineas]
except ValueError as e:
    print(f"Línea inválida ignorada: {e}")
    montos = []
else:
    print(f"{len(montos)} montos leídos")
finally:
    print("importación terminada")

Entrada para volver a pedir

El monto tecleado por Nabil pasa por float() dirigido: ValueError vuelve a pedir, el resto sube.

Exportación ausente

Falta el archivo del mes: FileNotFoundError muestra un mensaje claro en lugar de una traza roja.

Media sin venta

Ninguna venta en Argel ese día: ZeroDivisionError interceptado, «sin media» mostrado.

Monto prohibido

Un monto negativo dispara raise desde la Venta: fallar pronto, señalar en la fuente.

✓ Buenas prácticas

  • Enmarcar lo arriesgado preciso con bloques try estrechos.
  • Atrapar tipos precisos con un tratamiento real (else/finally con criterio).
  • Lanzar con raise para los invariantes; registrar y luego degradar correctamente.

✗ Errores frecuentes

  • Usar except: desnudo o except: pass (enmascara y entierra los bugs).
  • Enmarcar todo el programa en un solo try.
  • Silenciar en lugar de tratar (continuar con datos falsos).

Puntos clave

  • try/except atrapa los errores en lugar de dejar que el programa falle — bloque estrecho sobre lo arriesgado.
  • Dirija el tipo de error (ValueError, FileNotFoundError… ) en lugar de un except vacío.
  • else = lo siguiente si OK; finally se ejecuta siempre: perfecto para liberar un recurso.
  • raise lanza una excepción para señalar una situación anormal; SyntaxError no se gestiona con try.

Desafío 48h — aplíquelo mañana

Fiabilice un script real (el de Nabil o el suyo): try estrecho + except dirigido en cada punto frágil (conversión, archivo, división), un raise para un invariante, un finally real — y elimine todo except: pass.

Lista de salida de la sección
  • Puedo explicar try/except/else/finally y el papel de cada uno.
  • Sé atrapar preciso y lanzar con raise para mis invariantes.
  • He eliminado todo except desnudo o silenciado de mi código.
Autoevaluación 1-5: ¿mis try son estrechos y dirigidos?¿señalo pronto en lugar de silenciar?

Preguntas frecuentes

¿Hay que atrapar todos los errores con un except general?

No: un «except:» desnudo lo enmascara todo, incluidos bugs que debería ver. Atrape excepciones dirigidas (except ValueError) para gestionar solo lo que anticipa, y deje subir el resto. Es una regla de oro de la fiabilidad.

¿Qué diferencia hay entre un error de sintaxis y una excepción?

Un error de sintaxis (SyntaxError) impide que el programa arranque: el código está mal escrito. Una excepción ocurre en la ejecución de un código por lo demás válido (archivo ausente, división por cero). Solo las excepciones se gestionan con try/except.

Comprueba tu comprensión

¿Qué hay que evitar absolutamente en gestión de errores?

Recursos adicionales (opcional, para ir más allá)

Examen del nivel 2Preguntas inéditas y más difíciles para validar tus conocimientos