"Maneira correta de declarar exceções personalizadas no Python moderno?"
Isso é bom, a menos que sua exceção seja realmente um tipo de exceção mais específica:
class MyException(Exception):
pass
Ou melhor (talvez perfeito), em vez de pass
dar uma sequência de caracteres:
class MyException(Exception):
"""Raise for my specific kind of exception"""
Subclasses de exceção de subclasse
Dos documentos
Exception
Todas as exceções integradas que não saem do sistema são derivadas dessa classe. Todas as exceções definidas pelo usuário também devem ser derivadas dessa classe.
Isso significa que, se sua exceção for um tipo de exceção mais específica, subclasse essa exceção em vez da genérica Exception
(e o resultado será o de que você ainda deriva, Exception
conforme recomendado pela documentação). Além disso, você pode pelo menos fornecer uma string de documento (e não ser forçado a usar a pass
palavra - chave):
class MyAppValueError(ValueError):
'''Raise when my specific value is wrong'''
Defina os atributos que você mesmo cria com um costume __init__
. Evite passar um ditado como argumento posicional, os futuros usuários do seu código agradecerão. Se você usar o atributo de mensagem descontinuada, atribuir você mesmo evitará DeprecationWarning
:
class MyAppValueError(ValueError):
'''Raise when a specific subset of values in context of app is wrong'''
def __init__(self, message, foo, *args):
self.message = message # without this you may get DeprecationWarning
# Special attribute you desire with your Error,
# perhaps the value that caused the error?:
self.foo = foo
# allow users initialize misc. arguments as any other builtin Error
super(MyAppValueError, self).__init__(message, foo, *args)
Não há realmente nenhuma necessidade de escrever o seu próprio __str__
ou __repr__
. Os embutidos são muito legais e sua herança cooperativa garante que você o use.
Crítica da resposta principal
Talvez eu tenha perdido a pergunta, mas por que não:
class MyException(Exception):
pass
Novamente, o problema com o exposto acima é que, para capturá-lo, você precisará nomeá-lo especificamente (importando-o se criado em outro lugar) ou capturar Exception (mas você provavelmente não está preparado para lidar com todos os tipos de exceções, e você só deve pegar as exceções que está preparado para lidar). Críticas semelhantes às abaixo, mas, além disso, não é assim que se inicializa via super
, e você receberá uma DeprecationWarning
se acessar o atributo de mensagem:
Edit: para substituir algo (ou passar argumentos extras), faça o seguinte:
class ValidationError(Exception):
def __init__(self, message, errors):
# Call the base class constructor with the parameters it needs
super(ValidationError, self).__init__(message)
# Now for your custom code...
self.errors = errors
Dessa forma, você pode passar o dict de mensagens de erro para o segundo parâmetro e acessá-lo mais tarde com e.errors
Também requer exatamente dois argumentos a serem passados (além do self
.) Nem mais, nem menos. Essa é uma restrição interessante que os usuários futuros podem não apreciar.
Ser direto - viola a substituibilidade de Liskov .
Vou demonstrar os dois erros:
>>> ValidationError('foo', 'bar', 'baz').message
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
ValidationError('foo', 'bar', 'baz').message
TypeError: __init__() takes exactly 3 arguments (4 given)
>>> ValidationError('foo', 'bar').message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foo'
Comparado com:
>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'