Jak mogę zgłosić wyjątek w Pythonie, aby później mógł zostać przechwycony przez except
blok?
Jak ręcznie zgłosić / zgłosić wyjątek w Pythonie?
Użyj najbardziej szczegółowego konstruktora wyjątków, który semantycznie pasuje do Twojego problemu .
Bądź konkretny w swojej wiadomości, np .:
raise ValueError('A very specific bad thing happened.')
Unikaj generowania Exception
. Aby go złapać, musisz złapać wszystkie inne, bardziej szczegółowe wyjątki, które go dzielą.
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
Na przykład:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
A bardziej szczegółowe połowy nie złapią ogólnego wyjątku:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
raise
oświadczenieraise ValueError('A very specific bad thing happened')
co również pozwala na przekazanie dowolnej liczby argumentów do konstruktora:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
Dostęp do tych argumentów ma args
atrybut Exception
obiektu. Na przykład:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
odciski
('message', 'foo', 'bar', 'baz')
W Pythonie 2.5 dodano rzeczywisty message
atrybut na BaseException
rzecz zachęcania użytkowników do podklas wyjątków i zaprzestania ich używania args
, ale wprowadzenie message
i oryginalne wycofanie argumentów zostało wycofane .
except
klauzulaWewnątrz klauzuli wyjątku możesz na przykład zarejestrować, że wystąpił określony typ błędu, a następnie ponownie go podnieść. Najlepszym sposobem na to przy zachowaniu śladu stosu jest użycie instrukcji bare raise. Na przykład:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
Możesz zachować stacktrace (i wartość błędu) sys.exc_info()
, ale jest to o wiele bardziej podatne na błędy i ma problemy z kompatybilnością między Pythonem 2 i 3 , wolisz używać samego systemu raise
do przebijania.
Aby wyjaśnić - sys.exc_info()
zwraca typ, wartość i traceback.
type, value, traceback = sys.exc_info()
To jest składnia w Pythonie 2 - zauważ, że to nie jest kompatybilne z Pythonem 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
Jeśli chcesz, możesz zmodyfikować to, co dzieje się z Twoim nowym przebiciem - np. Ustawiając nowe args
dla instancji:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
I zachowaliśmy cały ślad podczas modyfikowania argumentów. Zauważ, że nie jest to najlepsza praktyka i jest to niepoprawna składnia w Pythonie 3 (utrudniająca zachowanie kompatybilności).
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
W Pythonie 3 :
raise error.with_traceback(sys.exc_info()[2])
Ponownie: unikaj ręcznej manipulacji śladami. Jest mniej wydajny i bardziej podatny na błędy. A jeśli używasz wątków i sys.exc_info
możesz nawet uzyskać niewłaściwe śledzenie (szczególnie jeśli używasz obsługi wyjątków dla przepływu sterowania - czego osobiście chciałbym uniknąć).
W Pythonie 3 można łączyć wyjątki, które zachowują identyfikatory:
raise RuntimeError('specific message') from error
Być świadomym:
Można je łatwo ukryć, a nawet dostać się do kodu produkcyjnego. Chcesz zgłosić wyjątek, a ich zrobienie spowoduje wyjątek, ale nie ten, który był przeznaczony!
Prawidłowy w Pythonie 2, ale nie w Pythonie 3, jest następujący:
raise ValueError, 'message' # Don't do this, it's deprecated!
Działa tylko w dużo starszych wersjach Pythona (2.4 i niższych), możesz nadal widzieć ludzi generujących ciągi znaków:
raise 'message' # really really wrong. don't do this.
We wszystkich nowoczesnych wersjach spowoduje to podniesienie a TypeError
, ponieważ nie podbijasz BaseException
typu. Jeśli nie sprawdzasz właściwego wyjątku i nie masz recenzenta, który jest świadomy problemu, może on zostać wprowadzony do produkcji.
Zgłaszam wyjątki, aby ostrzec konsumentów o moim interfejsie API, jeśli używają go niepoprawnie:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
„Chcę celowo popełnić błąd, aby przejść do wyjątku”
Możesz utworzyć własne typy błędów, jeśli chcesz wskazać, że coś jest nie tak z aplikacją, po prostu podklasuj odpowiedni punkt w hierarchii wyjątków:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
i użycie:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
raise
to, czego potrzebowałem, aby móc wykonać niestandardowe debugowanie błędów na wielu poziomach wykonania kodu bez przerywania śledzenia stosu.raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]
wydaje się robić to, co chcę i nigdy nie miałem z tym problemów. Ale wydaje się to zuchwałe i nie jest przyjętą praktyką. Czy jest lepszy sposób?Exception
twojej klasy nadrzędnej - możesz podklasować coś bardziej szczegółowego i powinieneś to zrobić, jeśli ma to sens.AppError
wyjątku. Może być lepiej użyć wbudowanego błędu, takiego jakAttributeError
Nie można uzyskać dużo bardziej pythonicznego niż to:
Zobacz dokumentację instrukcji wywoływania Pythona, jeśli chcesz uzyskać więcej informacji.
źródło
W Python3 istnieją 4 różne składnie wyjątków rasowania:
Jeśli użyjesz
raise exception (args)
do zgłoszenia wyjątku,args
zostanie on wydrukowany podczas drukowania obiektu wyjątku - jak pokazano w poniższym przykładzie.raise
instrukcja bez żadnych argumentów ponownie podnosi ostatni wyjątek. Jest to przydatne, jeśli po wykonaniu wyjątku trzeba wykonać pewne czynności, a następnie ponownie go podnieść. Ale jeśli wcześniej nie było wyjątku,raise
oświadczenie podnosiTypeError
wyjątek.Ta instrukcja służy do tworzenia łańcucha wyjątków, w którym wyjątek zgłoszony w odpowiedzi na inny wyjątek może zawierać szczegóły oryginalnego wyjątku - jak pokazano w poniższym przykładzie.
Wynik:
źródło
exception(args)
niżexception (args)
raise exception(args) from None
powiedzieć, że obecnie aktywny wyjątek został obsłużony i nie jest już interesujący. W przeciwnym razie, jeśli zgłosisz wyjątek wexcept
bloku i nie zostanie on obsłużony, identyfikatory śledzenia dla obu wyjątków zostaną pokazane oddzielone komunikatem „Podczas obsługi powyższego wyjątku wystąpił inny wyjątek”W typowym przypadku, w którym musisz rzucić wyjątek w odpowiedzi na niektóre nieoczekiwane warunki i którego nigdy nie zamierzasz złapać, ale po prostu zawieść szybko, aby umożliwić debugowanie stamtąd, jeśli kiedykolwiek się zdarzy - najbardziej logiczny wydaje się być
AssertionError
:źródło
ValueError
niżAssertionError
dlatego, że nie ma problemu z twierdzeniem (ponieważ nie ma tu miejsca) - problem dotyczy wartości. Jeśli naprawdę chceszAssertionError
w tym przypadku, napiszassert distance > 0, 'Distance must be positive'
. Ale nie powinieneś błędu sprawdzać w ten sposób, ponieważ asercje można wyłączyć (python -O
).-O
.Najpierw przeczytaj istniejące odpowiedzi, to tylko dodatek.
Zauważ, że możesz zgłaszać wyjątki z argumentami lub bez.
Przykład:
wychodzi z programu, ale możesz chcieć wiedzieć, co się stało, więc możesz tego użyć.
spowoduje to wydrukowanie „programu zakończonego” na stderr przed zamknięciem programu.
źródło
raise SystemExit()
byłby to lepszy wybór? Dlaczego pierwszy w ogóle działa?Innym sposobem na zgłoszenie wyjątku jest
assert
. Możesz użyć aser, aby sprawdzić, czy warunek jest spełniony, jeśli nie, to wzrośnieAssertionError
. Aby uzyskać więcej informacji, zobacz tutaj .źródło
Uwaga: zdarzają się chwile, kiedy chcesz obsłużyć ogólne wyjątki. Jeśli przetwarzasz kilka plików i rejestrujesz swoje błędy, możesz chcieć wychwycić każdy błąd, który wystąpi dla pliku, zaloguj go i kontynuuj przetwarzanie pozostałych plików. W takim przypadku a
zablokuj dobry sposób, aby to zrobić. Nadal będziesz chciał
raise
stosować wyjątki, abyś wiedział, co one oznaczają.źródło
W tym celu powinieneś nauczyć się instrukcji podnoszenia Pythona. Powinien być przechowywany w bloku try. Przykład -
źródło