"Une bonne façon de déclarer des exceptions personnalisées en Python moderne?"
C'est très bien, sauf si votre exception est vraiment un type d'exception plus spécifique:
class MyException(Exception):
pass
Ou mieux (peut-être parfait), au lieu de pass
donner une docstring:
class MyException(Exception):
"""Raise for my specific kind of exception"""
Sous-classes d'exception
À partir des documents
Exception
Toutes les exceptions intégrées qui ne sortent pas du système sont dérivées de cette classe. Toutes les exceptions définies par l'utilisateur doivent également être dérivées de cette classe.
Cela signifie que si votre exception est un type d'exception plus spécifique, sous-classe cette exception au lieu du générique Exception
(et le résultat sera que vous dérivez toujours Exception
comme le recommandent les documents). De plus, vous pouvez au moins fournir une docstring (et ne pas être obligé d'utiliser le pass
mot - clé):
class MyAppValueError(ValueError):
'''Raise when my specific value is wrong'''
Définissez les attributs que vous créez vous-même avec une personnalisation __init__
. Évitez de passer un dict comme argument positionnel, les futurs utilisateurs de votre code vous en remercieront. Si vous utilisez l'attribut de message obsolète, son attribution vous-même évitera 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)
Il n'y a vraiment pas besoin d'écrire votre propre __str__
ou __repr__
. Ceux intégrés sont très agréables, et votre héritage coopératif garantit que vous l'utilisez.
Critique de la meilleure réponse
J'ai peut-être manqué la question, mais pourquoi pas:
class MyException(Exception):
pass
Encore une fois, le problème avec ce qui précède est que pour l'attraper, vous devrez soit le nommer spécifiquement (l'importer s'il est créé ailleurs), soit intercepter l'exception, (mais vous n'êtes probablement pas prêt à gérer tous les types d'exceptions, et vous ne devez intercepter que les exceptions que vous êtes prêt à gérer). Critique similaire à ce qui suit, mais en plus, ce n'est pas la façon d'initialiser via super
, et vous obtiendrez un DeprecationWarning
si vous accédez à l'attribut de message:
Modifier: pour remplacer quelque chose (ou passer des arguments supplémentaires), procédez comme suit:
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
De cette façon, vous pouvez transmettre des messages d'erreur au deuxième paramètre et y accéder plus tard avec e.errors
Il faut également que deux arguments soient transmis (à part le self
.) Pas plus, pas moins. C'est une contrainte intéressante que les futurs utilisateurs pourraient ne pas apprécier.
Pour être direct - cela viole la substituabilité de Liskov .
Je vais démontrer les deux erreurs:
>>> 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'
Par rapport à:
>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'