Jaki jest powód istnienia try-else-else?
try
Blok pozwala na obsługę oczekiwany błąd. except
Blok powinien złapać tylko wyjątki jesteś przygotowany do obsługi. Jeśli poradzisz sobie z nieoczekiwanym błędem, Twój kod może zrobić coś złego i ukryć błędy.
else
Klauzula będzie wykonać, jeśli nie było żadnych błędów, a poprzez nie wykonanie tego kodu w try
bloku można uniknąć łapania nieoczekiwany błąd. Znów złapanie nieoczekiwanego błędu może ukryć błędy.
Przykład
Na przykład:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
return something
Pakiet „spróbuj, z wyjątkiem” zawiera dwie opcjonalne klauzule else
i finally
. Tak to jest właściwie try-except-else-finally
.
else
oceni tylko wtedy, gdy nie ma wyjątku od try
bloku. Pozwala nam uprościć bardziej skomplikowany kod poniżej:
no_error = None
try:
try_this(whatever)
no_error = True
except SomeException as the_exception:
handle(the_exception)
if no_error:
return something
więc jeśli porównamy else
alternatywę (która może powodować błędy), zobaczymy, że zmniejsza ona liczbę wierszy kodu i możemy mieć bardziej czytelną, łatwą w utrzymaniu i mniej wadliwą bazę kodu.
finally
finally
wykona się bez względu na wszystko, nawet jeśli inny wiersz jest oceniany za pomocą instrukcji return.
Podział na pseudo-kod
Może to pomóc w rozbiciu tego, w możliwie najmniejszej formie, która pokazuje wszystkie funkcje, z komentarzami. Załóżmy, że pseudo-kod jest poprawny pod względem składniowym (ale nie można go uruchomić, chyba że zdefiniowane są nazwy).
Na przykład:
try:
try_this(whatever)
except SomeException as the_exception:
handle_SomeException(the_exception)
# Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
generic_handle(the_exception)
# Handle any other exception that inherits from Exception
# - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
# Avoid bare `except:`
else: # there was no exception whatsoever
return something()
# if no exception, the "something()" gets evaluated,
# but the return will not be executed due to the return in the
# finally block below.
finally:
# this block will execute no matter what, even if no exception,
# after "something" is eval'd but before that value is returned
# but even if there is an exception.
# a return here will hijack the return functionality. e.g.:
return True # hijacks the return in the else clause above
Prawdą jest, że zamiast tego moglibyśmy umieścić kod w else
bloku w try
bloku, gdzie byłby uruchamiany, gdyby nie było wyjątków, ale co, jeśli sam kod wywoła wyjątek, który łapiemy? Pozostawienie go w try
bloku ukryłoby ten błąd.
Chcemy zminimalizować wiersze kodu w try
bloku, aby uniknąć przechwytywania wyjątków, których się nie spodziewaliśmy, zgodnie z zasadą, że jeśli nasz kod zawiedzie, chcemy, aby zawodził głośno. To najlepsza praktyka .
Rozumiem, że wyjątki nie są błędami
W Pythonie większość wyjątków stanowią błędy.
Możemy wyświetlić hierarchię wyjątków za pomocą pydoc. Na przykład w Python 2:
$ python -m pydoc exceptions
lub Python 3:
$ python -m pydoc builtins
Daje nam hierarchię. Widzimy, że większość rodzajów Exception
błędów to błędy, chociaż Python używa niektórych z nich do takich rzeczy jak kończenie for
pętli ( StopIteration
). Oto hierarchia Pythona 3:
BaseException
Exception
ArithmeticError
FloatingPointError
OverflowError
ZeroDivisionError
AssertionError
AttributeError
BufferError
EOFError
ImportError
ModuleNotFoundError
LookupError
IndexError
KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError
ConnectionAbortedError
ConnectionRefusedError
ConnectionResetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
ProcessLookupError
TimeoutError
ReferenceError
RuntimeError
NotImplementedError
RecursionError
StopAsyncIteration
StopIteration
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError
UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning
DeprecationWarning
FutureWarning
ImportWarning
PendingDeprecationWarning
ResourceWarning
RuntimeWarning
SyntaxWarning
UnicodeWarning
UserWarning
GeneratorExit
KeyboardInterrupt
SystemExit
Komentator zapytał:
Powiedzmy, że masz metodę, która wysyła ping do zewnętrznego API i chcesz obsłużyć wyjątek w klasie spoza opakowania API, czy po prostu zwracasz e z metody w ramach klauzuli wyjątku, gdzie e jest obiektem wyjątku?
Nie, nie raise
zwrócisz wyjątku, po prostu ponownie go unieś, aby zachować ślad stosu.
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise
Lub, w Python 3, możesz zgłosić nowy wyjątek i zachować ślad za pomocą łączenia wyjątków:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise DifferentException from the_exception
Rozwijam tutaj swoją odpowiedź .