„Właściwy sposób deklarowania niestandardowych wyjątków we współczesnym Pythonie?”
Jest to w porządku, chyba że twój wyjątek jest naprawdę bardziej szczegółowym wyjątkiem:
class MyException(Exception):
pass
Lub lepiej (może idealnie), zamiast pass
podawać dokumenty:
class MyException(Exception):
"""Raise for my specific kind of exception"""
Podklasy Wyjątek Podklasy
Z dokumentów
Exception
Wszystkie wbudowane wyjątki nie będące wyjściem z systemu pochodzą z tej klasy. Wszystkie wyjątki zdefiniowane przez użytkownika powinny również pochodzić z tej klasy.
Oznacza to, że jeśli twój wyjątek jest rodzajem bardziej szczegółowego wyjątku, podklasuj ten wyjątek zamiast ogólnego Exception
(a wynik będzie taki, że nadal wywodzisz się z niego, Exception
jak zalecają dokumenty). Możesz także podać dokument (i nie zmuszać Cię do używania pass
słowa kluczowego):
class MyAppValueError(ValueError):
'''Raise when my specific value is wrong'''
Ustaw atrybuty, które sam tworzysz za pomocą niestandardowego __init__
. Unikaj przekazywania dyktusa jako argumentu pozycyjnego, przyszli użytkownicy Twojego kodu będą Ci wdzięczni. Jeśli użyjesz przestarzałego atrybutu wiadomości, samodzielne przypisanie go pozwoli uniknąć 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)
Naprawdę nie trzeba pisać własnego __str__
lub __repr__
. Wbudowane są bardzo ładne, a twoje wspólne dziedzictwo zapewnia, że z niego korzystasz.
Krytyka najwyższej odpowiedzi
Może przegapiłem pytanie, ale dlaczego nie:
class MyException(Exception):
pass
Ponownie, problem z powyższym polega na tym, że aby go złapać, musisz albo nazwać go konkretnie (zaimportować, jeśli został utworzony gdzie indziej), albo złapać wyjątek (ale prawdopodobnie nie jesteś przygotowany do obsługi wszystkich rodzajów wyjątków, i powinieneś wychwytywać tylko wyjątki, na które jesteś przygotowany). Podobna krytyka do poniższej, ale dodatkowo nie jest to sposób inicjowania za pośrednictwem super
, a otrzymasz DeprecationWarning
dostęp, jeśli uzyskasz dostęp do atrybutu wiadomości:
Edycja: aby zastąpić coś (lub przekazać dodatkowe argumenty), wykonaj następujące czynności:
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
W ten sposób możesz przekazać dyktę komunikatów o błędach do drugiego parametru i przejść do niego później z e.errors
Wymaga również przekazania dokładnie dwóch argumentów (oprócz self
.) Nie więcej, nie mniej. To interesujące ograniczenie, którego przyszli użytkownicy mogą nie docenić.
Mówiąc wprost - narusza to substytucyjność Liskova .
Zademonstruję oba błędy:
>>> 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'
W porównaniu do:
>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'