7.1 Criando Exceções Personalizadas
Às vezes, as exceções padrão do Python não satisfazem completamente as necessidades da tua aplicação. Nesses casos, você pode criar suas próprias exceções, herdando-as da classe base Exception ou qualquer outra classe de exceção apropriada.
Fundamentos de criação de exceções personalizadas
Criar uma exceção personalizada envolve definir uma nova classe que herda da classe base Exception. Você pode adicionar seus próprios métodos e atributos na sua classe de exceção para fornecer informações adicionais sobre o erro.
Exemplo de criação de uma exceção personalizada simples
Passo 1: Definição da Exceção Personalizada
class MyCustomError(Exception):
"""Classe para exceção personalizada."""
pass
Passo 2: Uso da Exceção Personalizada
def check_value(value):
if value < 0:
raise MyCustomError("O valor não deve ser menor que zero")
try:
check_value(-1)
except MyCustomError as e:
print(f"Ocorreu uma exceção personalizada: {e}")
É bem simples. O principal é que sua exceção seja herdada da classe Exception ou de um dos seus descendentes.
7.2 Criando Exceções com Atributos Adicionais
Você pode adicionar atributos e métodos na sua classe de exceção para passar informações adicionais sobre o erro que ocorreu.
Exemplo:
class NegativeValueError( Exception ):
"""Classe para exceção personalizada ao valor ser negativo."""
def __init__(self, value, message = "O valor não deve ser menor que zero"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
Uso da Exceção com Atributos Adicionais
def check_value(value):
if value < 0:
raise NegativeValueError(value)
try:
check_value(-1)
except NegativeValueError as e:
print(f"Ocorreu uma exceção personalizada: {e}")
Nossa exceção é uma classe que herda da classe Exception. Portanto, com ela você pode fazer tudo o que poderia com qualquer outra classe: adicionar campos, métodos, parâmetros no construtor etc.
Tudo para a sua conveniência, e a conveniência dos programadores que vão capturar suas exceções.
7.3 Criando Hierarquias de Exceções Personalizadas
Para aplicações mais complexas, é útil criar hierarquias de exceções personalizadas. Isso permite agrupar exceções relacionadas e facilita o tratamento delas.
Exemplo:
class ApplicationError(Exception):
"""Classe base para todas exceções de aplicação."""
pass
class NegativeValueError(ApplicationError):
"""Classe para exceção personalizada ao valor ser negativo."""
def __init__(self, value, message="O valor não deve ser menor que zero"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
class ValueTooLargeError(ApplicationError):
"""Classe para exceção personalizada ao valor ser muito grande."""
def __init__(self, value, message="O valor é muito grande"):
self.value = value
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.message}: {self.value}'
Uso da Hierarquia de Exceções 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"Ocorreu uma exceção: {e}")
except ValueTooLargeError as e:
print(f"Ocorreu uma exceção: {e}")
except ApplicationError as e:
print(f"Exceção geral da aplicação: {e}")
7.4 Ordem de Captura de Exceções
Ao capturar exceções, especialmente de uma mesma hierarquia, é importante especificar a ordem correta delas. Embora o código dentro dos blocos except nunca seja executado simultaneamente, a questão é que a classe base da exceção pode capturar exceções de todas as classes descendentes.
Por exemplo, o 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á exceções do tipo ApplicationError e todos seus descendentes
print(f"Exceção geral da aplicação: {e}")
No bloco except serão capturadas exceções do tipo ApplicationError e de todas as suas classes descendentes.
E como todas as exceções são descendentes da classe Exception, esse código capturará todas as exceções:
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 as exceções: {e}")
except ApplicationError as e: # Este código nunca será executado
print(f"Exceção geral da aplicação: {e}")
A captura de exceções no bloco except ApplicationError nunca ocorrerá, pois o bloco except Exception capturará todas as exceções do tipo Exception e quaisquer descendentes.
Solução
Portanto, é costume capturar exceções na ordem inversa à herança: quanto mais próximo o classe estiver da classe Exception, mais abaixo ele deve ficar.
Exemplo:
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"Ocorreu uma exceção: {e}")
except ApplicationError as e:
print(f"Exceção geral da aplicação: {e}")
except Exception as e:
print(f"Captura de todas as exceções: {e}")
Neste exemplo, os blocos except estão organizados na ordem que corresponde à sua hierarquia de herança: primeiro, são capturadas exceções mais específicas, como NegativeValueError, depois as mais gerais, como ApplicationError. Isso permite que as exceções sejam tratadas adequadamente e evita situações em que um manipulador mais geral captura a exceção antes que os blocos except mais especializados possam processá-la.
GO TO FULL VERSION