7.1 Creación de excepciones personalizadas
A veces las excepciones estándar de Python no satisfacen completamente
las necesidades de tu aplicación. En tales casos, puedes crear
tus propias excepciones heredándolas de la clase base
Exception
o de cualquier otra clase de excepciones
adecuada.
Fundamentos de la creación de excepciones personalizadas
Crear una excepción personalizada implica definir
una nueva clase que se hereda de la clase base
Exception
. Puedes añadir tus propios métodos y
atributos a tu clase de excepción para proporcionar información
adicional sobre el error.
Ejemplo de creación de una excepción personalizada simple
Paso 1: Definición de la excepción personalizada
class MyCustomError(Exception):
"""Clase para la excepción personalizada."""
pass
Paso 2: Uso de la excepción personalizada
def check_value(value):
if value < 0:
raise MyCustomError("El valor no debe ser menor que cero")
try:
check_value(-1)
except MyCustomError as e:
print(f"Ocurrió una excepción personalizada: {e}")
Todo es muy sencillo. Lo principal es que tu excepción debe heredarse de la clase Exception
o de uno de sus
descendientes.
7.2 Creación de excepciones con atributos adicionales
Puedes añadir atributos y métodos a tu clase de excepción para pasar información adicional sobre el error que ocurrió.
Ejemplo:
class NegativeValueError(Exception):
"""Clase para la excepción personalizada en caso de valor negativo."""
def __init__(self, value, message = "El valor no debe ser menor que cero"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
Uso de excepción con atributos adicionales
def check_value(value):
if value < 0:
raise NegativeValueError(value)
try:
check_value(-1)
except NegativeValueError as e:
print(f"Ocurrió una excepción personalizada: {e}")
Nuestra excepción es una clase que hereda de la clase Exception
. Por lo tanto, puedes hacer todo lo que
harías con cualquier otra clase: agregar campos, métodos, parámetros de constructor, etc.
Todo para tu comodidad, y la comodidad de los programadores que interceptarán tus excepciones.
7.3 Creación de jerarquía de excepciones personalizadas
Para aplicaciones más complejas, es útil crear jerarquías de excepciones personalizadas. Esto permite agrupar excepciones relacionadas y simplificar su manejo.
Ejemplo:
class ApplicationError(Exception):
"""Clase base para todas las excepciones de la aplicación."""
pass
class NegativeValueError(ApplicationError):
"""Clase para la excepción personalizada en caso de valor negativo."""
def __init__(self, value, message="El valor no debe ser menor que cero"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
class ValueTooLargeError(ApplicationError):
"""Clase para la excepción personalizada en caso de valor demasiado grande."""
def __init__(self, value, message="El valor es demasiado grande"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
Uso de jerarquía de excepciones personalizadas
def check_value(value):
if value < 0:
raise NegativeValueError(value)
elif value > 100:
raise ValueTooLargeError(value)
try:
check_value(150)
except NegativeValueError as e:
print(f"Ocurrió una excepción: {e}")
except ValueTooLargeError as e:
print(f"Ocurrió una excepción: {e}")
except ApplicationError as e:
print(f"Excepción general de la aplicación: {e}")
7.4 Orden de captura de excepciones
Al capturar excepciones, especialmente de una misma jerarquía, es importante especificar su orden correcto. Y aunque el código dentro de
los bloques except
nunca se ejecuta simultáneamente, la cuestión es que la clase base de excepciones puede
capturar excepciones de todas las clases derivadas.
Por ejemplo, el código:
def check_value(value):
if value < 0:
raise NegativeValueError(value)
elif value > 100:
raise ValueTooLargeError(value)
try:
check_value(150)
except ApplicationError as e: # capturará excepciones del tipo ApplicationError y todos sus descendientes
print(f"Excepción general de la aplicación: {e}")
En el bloque except
se capturarán excepciones del tipo ApplicationError
y todos sus clases derivadas.
Y dado que todas las excepciones son descendientes de la clase Exception
, tal código capturará todas las excepciones:
def check_value(value):
if value < 0:
raise NegativeValueError(value)
elif value > 100:
raise ValueTooLargeError(value)
try:
check_value(150)
except Exception as e:
print(f"Captura de todas las excepciones: {e}")
except ApplicationError as e: # Este código nunca se ejecutará
print(f"Excepción general de la aplicación: {e}")
La captura de excepciones en el bloque except ApplicationError
nunca ocurrirá, ya que el bloque except Exception
capturará
todas las excepciones del tipo Exception
y cualquiera de sus descendientes.
Solución
Por lo tanto, es costumbre capturar excepciones en el orden inverso al de la herencia: cuanto más cerca esté la clase de la clase Exception
, más
abajo se encuentra.
Ejemplo:
def check_value(value):
if value < 0:
raise NegativeValueError(value)
elif value > 100:
raise ValueTooLargeError(value)
try:
check_value(150)
except NegativeValueError as e:
print(f"Ocurrió una excepción: {e}")
except ApplicationError as e:
print(f"Excepción general de la aplicación: {e}")
except Exception as e:
print(f"Captura de todas las excepciones: {e}")
En este ejemplo, los bloques except
están dispuestos en el orden que corresponde a su jerarquía de herencia:
primero se capturan las excepciones más específicas, como NegativeValueError
, luego las más generales, como ApplicationError
.
Esto permite manejar correctamente las excepciones y evitar situaciones en las que un manejador más general capture una excepción antes de que puedan manejarla bloques except
más especializados.
GO TO FULL VERSION