Creación de excepciones personalizadas
Introducción
En Python, las excepciones estándar cubren una amplia variedad de errores comunes, desde divisiones por cero hasta problemas de importación de módulos. Sin embargo, en muchas aplicaciones necesitamos comunicar errores específicos de nuestro dominio de problema. Las excepciones personalizadas nos permiten definir y manejar situaciones de error propias de nuestra aplicación, mejorando la estructura del código y facilitando la depuración. En este artículo, aprenderemos a crear nuestras propias clases de excepción y a utilizarlas eficazmente en nuestros programas.
¿Por qué crear excepciones personalizadas?
Las excepciones personalizadas ofrecen varias ventajas:
- Semántica clara: Comunican de forma precisa qué ha fallado en nuestro código
- Jerarquía de errores: Permiten estructurar los errores según su tipo o severidad
- Manejo específico: Facilitan el tratamiento diferenciado de distintos errores
- Documentación implícita: Ayudan a entender qué puede fallar en una función
Creación básica de una excepción personalizada
Una excepción personalizada es simplemente una clase que hereda de alguna clase de excepción existente, normalmente Exception
:
class MiExcepcion(Exception):
pass
# Uso básico
def funcion_problematica():
raise MiExcepcion("Ha ocurrido un error personalizado")
try:
funcion_problematica()
except MiExcepcion as error:
print(f"Capturada la excepción: {error}")
Aunque este ejemplo es funcional, podemos mejorar nuestras excepciones personalizadas añadiendo más funcionalidad.
Excepciones personalizadas avanzadas
Podemos crear excepciones más sofisticadas que almacenen información adicional sobre el error:
class ErrorDeDatos(Exception):
"""Excepción para errores relacionados con datos de entrada."""
def __init__(self, mensaje, valor, sugerencia=None):
self.mensaje = mensaje
self.valor = valor
self.sugerencia = sugerencia
# Llamamos al constructor de la clase padre
super().__init__(self.mensaje)
def __str__(self):
resultado = f"{self.mensaje}: {self.valor}"
if self.sugerencia:
resultado += f" (Sugerencia: {self.sugerencia})"
return resultado
# Ejemplo de uso
def validar_edad(edad):
if not isinstance(edad, int):
raise ErrorDeDatos("La edad debe ser un número entero", edad,
"Intenta convertir el valor a entero con int()")
if edad < 0:
raise ErrorDeDatos("La edad no puede ser negativa", edad,
"Los valores válidos deben ser mayores o iguales a 0")
if edad > 120:
raise ErrorDeDatos("La edad parece demasiado alta", edad,
"Verifica que el valor sea correcto")
return True
# Probamos la función
try:
validar_edad("veinticinco")
except ErrorDeDatos as error:
print(error)
Jerarquía de excepciones personalizadas
Podemos crear una estructura jerárquica de excepciones para representar diferentes tipos o categorías de errores:
# Excepción base para nuestra aplicación
class ErrorAplicacion(Exception):
"""Clase base para todas las excepciones de la aplicación."""
pass
# Excepciones específicas que heredan de nuestra excepción base
class ErrorDeBaseDeDatos(ErrorAplicacion):
"""Errores relacionados con la base de datos."""
pass
class ErrorDeConexion(ErrorAplicacion):
"""Errores relacionados con conexiones de red."""
pass
class ErrorDeAutenticacion(ErrorAplicacion):
"""Errores relacionados con la autenticación de usuarios."""
pass
# Excepciones aún más específicas
class ErrorConsultaSQL(ErrorDeBaseDeDatos):
"""Error específico para consultas SQL incorrectas."""
def __init__(self, consulta, mensaje):
self.consulta = consulta
super().__init__(f"Error en consulta SQL: {mensaje}. Consulta: {consulta}")
Esta jerarquía nos permite capturar excepciones a diferentes niveles según sea necesario:
def ejecutar_operacion():
# Simulamos un error de consulta SQL
raise ErrorConsultaSQL("SELECT * FORM usuarios", "Sintaxis incorrecta en la consulta")
try:
ejecutar_operacion()
except ErrorConsultaSQL as error:
print(f"Error específico de SQL: {error}")
except ErrorDeBaseDeDatos as error:
print(f"Error general de base de datos: {error}")
except ErrorAplicacion as error:
print(f"Error general de la aplicación: {error}")
Excepciones desde excepciones estándar
También podemos crear excepciones personalizadas que hereden de excepciones estándar más específicas que Exception
:
class ValorFueraDeLimites(ValueError):
"""Excepción para valores que están fuera de un rango esperado."""
pass
class ArchivoConfiguracionInvalido(FileNotFoundError):
"""Excepción para cuando un archivo de configuración no existe o está mal formado."""
pass
# Ejemplo de uso
def obtener_configuracion(ruta_archivo):
try:
if not ruta_archivo.endswith('.conf'):
raise ArchivoConfiguracionInvalido(f"El archivo {ruta_archivo} no tiene extensión .conf")
# Aquí iría el código para leer el archivo
except FileNotFoundError:
raise ArchivoConfiguracionInvalido(f"No se encontró el archivo de configuración: {ruta_archivo}")
Cuándo crear excepciones personalizadas
Es recomendable crear excepciones personalizadas cuando:
- Los errores son específicos de tu dominio de aplicación
- Necesitas transportar información adicional sobre el error
- Quieres distinguir claramente entre diferentes tipos de errores
- Estás desarrollando una biblioteca o framework que será usado por otros
Por otro lado, si un error ya está bien representado por una excepción estándar de Python, generalmente es mejor usar esa excepción en lugar de crear una nueva.
Documentación de excepciones personalizadas
Es importante documentar adecuadamente nuestras excepciones personalizadas:
class InsuficienteSaldo(Exception):
"""
Excepción lanzada cuando se intenta realizar una operación
con un saldo insuficiente.
Atributos:
saldo -- saldo actual disponible
cantidad -- cantidad que se intentó retirar o utilizar
mensaje -- explicación del error
"""
def __init__(self, saldo, cantidad, mensaje="Saldo insuficiente"):
self.saldo = saldo
self.cantidad = cantidad
self.mensaje = mensaje
self.deficit = cantidad - saldo
super().__init__(self.mensaje)
def __str__(self):
return f"{self.mensaje}: se intentó utilizar {self.cantidad} pero solo hay disponible {self.saldo}"
Resumen
Las excepciones personalizadas son una herramienta poderosa para comunicar y manejar errores específicos de nuestras aplicaciones. Creando una jerarquía adecuada de excepciones, podemos estructurar mejor el manejo de errores, incluir información relevante para la depuración, y hacer que nuestro código sea más expresivo y fácil de mantener. Recuerda siempre documentar tus excepciones personalizadas para que otros programadores (incluyendo tu yo del futuro) puedan entender claramente su propósito y funcionamiento. En el siguiente artículo, exploraremos las buenas prácticas para el manejo de errores en Python.