Czy używanie try-except-else w Pythonie jest dobrą praktyką?
Od czasu do czasu w Pythonie widzę blok:
try:
try_this(whatever)
except SomeException as exception:
#Handle exception
else:
return something
Jaki jest powód, dla którego próba-z wyjątkiem-innego-istnieje?
Nie lubię tego rodzaju programowania, ponieważ używa WYJĄTKÓW do sterowania przepływem. Jeśli jednak jest on zawarty w języku, musi być ku temu dobry powód, prawda?
Rozumiem, że wyjątki nie są błędami, i że powinny być używane tylko w wyjątkowych warunkach (np. staram się zapisać plik do dysk i nie ma więcej miejsca, a może nie mam uprawnień), a nie do kontroli przepływu.
Zwykle obsługuję wyjątki jako:
something = some_default_value
try:
something = try_this(whatever)
except SomeException as exception:
#Handle exception
finally:
return something
Lub jeśli naprawdę nie chcę zwracać niczego, jeśli wydarzy się wyjątek, to:
try:
something = try_this(whatever)
return something
except SomeException as exception:
#Handle exception
9 answers
"Nie wiem, czy to z niewiedzy, ale nie podoba mi się to rodzaj programowania, jak to jest za pomocą WYJĄTKÓW do wykonywania kontroli przepływu."
W świecie Pythona używanie WYJĄTKÓW do kontroli przepływu jest powszechne i normalne.
Nawet Programiści Pythona używają WYJĄTKÓW do sterowania przepływem i ten styl jest mocno upieczony w języku (tzn. protokół iteratora używa Stopiteracja do zakończenia pętli sygnału).
Dodatkowo, styl try-except jest używany, aby zapobiec Warunkom wyścigu związanym z niektórymi konstrukcjami "look-before-you-leap". Na przykład testowanie os./ align = "left" / istnieje wyniki w informacji, które mogą być nieaktualne do czasu korzystania z nich. Podobnie, Kolejkapełne zwraca informacje, które mogą być nieświeże. Styl try-except-else stworzy bardziej niezawodny kod w takich przypadkach.
W niektórych innych językach reguła ta odzwierciedla ich normy kulturowe, które znajdują odzwierciedlenie w ich bibliotekach. "Reguła" opiera się również częściowo na względach wydajności dla tych języków."rozumiem, że wyjątki nie są błędy, powinny tylko być stosowane w wyjątkowych warunkach "
Norma kulturowa Pythona jest nieco inna. W wielu przypadkach musisz używać WYJĄTKÓW do sterowania przepływem. Ponadto, użycie WYJĄTKÓW w Pythonie nie spowalnia otaczającego kodu i kodu wywołującego, jak to ma miejsce w niektórych języki skompilowane (np. CPython implementuje już kod do sprawdzania WYJĄTKÓW na każdym kroku, niezależnie od tego, czy faktycznie używasz WYJĄTKÓW, czy nie).
Innymi słowy, twoje zrozumienie, że "wyjątki są dla wyjątków" jest regułą, która ma sens w niektórych innych językach, ale nie w Pythonie." Jednakże, jeśli jest on zawarty w samym języku, musi istnieć dobry powód, prawda?"
Oprócz pomocy w unikaniu race-warunki, wyjątki są również bardzo przydatne do wyciągania błędów obsługi pętli zewnętrznych. Jest to niezbędna optymalizacja w językach interpretowanych, które nie mają tendencji do automatycznego niezmienniczego ruchu kodu pętli .
Wyjątki mogą również znacznie uprościć kod w typowych sytuacjach, gdy możliwość obsługi problemu jest daleko od miejsca, w którym problem powstał. Na przykład, jest to powszechne, aby mieć kod wywołujący najwyższego poziomu interfejsu użytkownika dla logiki biznesowej, która z kolei nazywa się niskopoziomowe procedury. Sytuacje pojawiające się w procedurach niskiego poziomu (takich jak duplikaty rekordów dla unikalnych kluczy w dostępie do bazy danych) mogą być obsługiwane tylko w kodzie najwyższego poziomu (np. poproszenie użytkownika o nowy klucz, który nie jest sprzeczny z istniejącymi kluczami). Użycie wyjątków dla tego rodzaju przepływu sterowania pozwala procedurom średniego poziomu całkowicie zignorować problem i być ładnie oddzielonym od tego aspektu kontroli przepływu.
Jest ładny wpis na blogu o niezbędności wyjątki tutaj .
Zobacz też tę odpowiedź przepełnienia stosu: czy wyjątki są naprawdę wyjątkowymi błędami?
"Jaki jest powód, dla którego próba-z wyjątkiem-innego istnieje?"
Sama klauzula else jest interesująca. Działa, gdy nie ma wyjątku, ale przed finally-clause. To jest jego główny cel.
Bez klauzuli else, jedyną opcją uruchomienia dodatkowego kodu przed finalizacją byłaby niezdarna praktyka dodawania kodu do klauzuli próbnej. To jest niezdarne, bo grozi zgłaszanie WYJĄTKÓW w kodzie, który nie miał być chroniony przez try-block.
Przypadek użycia dodatkowego niezabezpieczonego kodu przed finalizacją nie pojawia się zbyt często. Nie spodziewaj się więc, że zobaczysz wiele przykładów w opublikowanym kodzie. Jest dość rzadki.
Innym przypadkiem użycia klauzuli else jest wykonywanie akcji, które muszą wystąpić, gdy nie występuje wyjątek i które nie występują, gdy wyjątki są obsługiwane. Na przykład:
recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')
Na koniec, najczęstszym zastosowaniem klauzuli else w bloku try jest upiększanie (wyrównywanie wyników wyjątkowych i nie-wyjątkowych na tym samym poziomie wcięć). To użycie jest zawsze opcjonalne i nie jest bezwzględnie konieczne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-31 01:10:48
Jaki jest powód, dla którego próba-z wyjątkiem-innego-istnieje?
Blok try
pozwala obsłużyć oczekiwany błąd. Blok except
powinien przechwytywać tylko wyjątki, z którymi jesteś przygotowany. Jeśli obsłużysz nieoczekiwany błąd, Twój kod może zrobić coś złego i ukryć błędy.
Klauzula else
zostanie wykonana, jeśli nie było błędów, a nie wykonując tego kodu w bloku try
, unikniesz wychwycenia nieoczekiwanego błędu. Ponownie, wyłapanie nieoczekiwanego błędu może Ukryj robaki.
Przykład
Na przykład:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
return something
Pakiet "try, except" ma dwie opcjonalne klauzule, else
i finally
. Więc to właściwie try-except-else-finally
.
else
będzie oceniać tylko wtedy, gdy nie ma wyjątku od bloku try
. 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
do alternatywy (która może powodować błędy) widzimy, że zmniejsza to linie kodu i możemy mieć bardziej czytelny, możliwy do utrzymania, i mniej błędnych kodów.
finally
finally
wykonaÄ ‡ bez wzglÄ ™ du na wszystko, nawet jeĹ " li inna linia jest oceniana za pomocÄ ... instrukcji return.
W podziale na pseudo-kod
To może pomóc rozbić to, w najmniejszej możliwej formie, która demonstruje wszystkie funkcje, z komentarzami. Załóżmy, że jest to poprawne składniowo (ale nie można go uruchomić, jeśli nazwy nie są zdefiniowane) pseudo-kod jest w funkcji.
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
To prawda że możemy umieścić kod w bloku else
w bloku try
zamiast tego, gdzie by działał, gdyby nie było wyjątków, ale co jeśli sam kod wywoła wyjątek tego rodzaju, który wyłapujemy? Pozostawienie go w bloku try
ukryłoby ten błąd.
Chcemy zminimalizować linie kodu w bloku try
, aby uniknąć wychwytywania WYJĄTKÓW, których się nie spodziewaliśmy, zgodnie z zasadą, że jeśli nasz kod zawiedzie, chcemy, aby zawieść głośno. To jest najlepsze praktyka .
Rozumiem, że wyjątki nie są błędami
W Pythonie większość WYJĄTKÓW to błędy.
Możemy wyświetlić hierarchię WYJĄTKÓW za pomocą pydoc. Na przykład w Pythonie 2:
$ python -m pydoc exceptions
Lub Python 3:
$ python -m pydoc builtins
Da nam hierarchię. Widzimy, że większość rodzajów Exception
to błędy, chociaż Python używa niektórych z nich do takich rzeczy, jak kończenie for
pętli (StopIteration
). To jest Python 3 ' s hierarchia:
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 pingi zewnętrzne API i chcesz obsłużyć wyjątek w klasie poza opakowaniem API, czy po prostu zwracasz e Z metody pod klauzulą Exception, gdzie E jest obiektem wyjątku?
Nie, Nie zwracasz wyjątku, po prostu przekaż go gołym raise
, aby zachować stacktrace.
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise
Lub, w Pythonie 3, możesz podnieść nowy wyjątek i zachować backtrace z wyjątkiem chaining:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise DifferentException from the_exception
I rozwinąć w moja odpowiedź tutaj .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-31 01:14:06
Python nie zgadza się z ideą, że wyjątki powinny być używane tylko w wyjątkowych przypadkach, w rzeczywistości idiom to 'proś o przebaczenie, a nie pozwolenie'. Oznacza to, że używanie WYJĄTKÓW jako rutynowej części kontroli przepływu jest całkowicie dopuszczalne i w rzeczywistości zachęcane.
Jest to ogólnie dobra rzecz, ponieważ praca w ten sposób pomaga uniknąć pewnych problemów( jako oczywisty przykład, często unika się warunków wyścigowych), a to sprawia, że kod jest nieco bardziej czytelne.
Wyobraź sobie, że masz sytuację, w której pobierasz dane użytkownika, które muszą być przetworzone, ale masz domyślną wartość, która jest już przetworzona. Struktura try: ... except: ... else: ...
sprawia, że kod jest bardzo czytelny:
try:
raw_value = int(input())
except ValueError:
value = some_processed_value
else: # no error occured
value = process_value(raw_value)
Porównaj z tym, jak może działać w innych językach:
raw_value = input()
if valid_number(raw_value):
value = process_value(int(raw_value))
else:
value = some_processed_value
Zwróć uwagę na zalety. Nie ma potrzeby sprawdzania poprawności wartości i analizowania jej oddzielnie, są one wykonywane raz. Kod również postępuje bardziej logicznie, główną ścieżką kodu jest pierwsza, po której następuje " if to nie działa, zrób to.
Przykład jest oczywiście trochę wymyślony, ale pokazuje, że istnieją przypadki dla tej struktury.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-22 01:59:14
Czy używanie try-except-else w Pythonie jest dobrą praktyką?
Odpowiedź jest taka, że jest zależna od kontekstu. Jeśli to zrobisz:
d = dict()
try:
item = d['item']
except KeyError:
item = 'default'
To pokazuje, że nie znasz Pythona zbyt dobrze. Ta funkcjonalność jest zamknięta w metodzie dict.get
:
item = d.get('item', 'default')
The try
/except
block to znacznie bardziej zaśmiecony wizualnie i wyrazisty sposób pisania tego, co można efektywnie wykonać w jednej linii za pomocą metody atomowej. Istnieją inne przypadki, w których to prawda.
Nie oznacza to jednak, że powinniśmy unikać obsługi wyjątków. W niektórych przypadkach preferowane jest unikanie warunków wyścigowych. Nie sprawdzaj, czy plik istnieje, po prostu spróbuj go otworzyć i złap odpowiedni IOError. W trosce o prostotę i czytelność spróbuj to ująć lub potraktować jako apropos.
[[6]}przeczytaj Zen Pythona, rozumiejąc, że istnieją zasady, które są w napięciu, i uważaj na dogmat, który opiera się zbyt mocno na każde z zawartych w nim stwierdzeń.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-03 16:54:25
Należy uważać na użycie bloku finally, ponieważ nie jest to to samo, co użycie bloku else w próbie, except. Blok finally zostanie uruchomiony niezależnie od wyniku próby except.
In [10]: dict_ = {"a": 1}
In [11]: try:
....: dict_["b"]
....: except KeyError:
....: pass
....: finally:
....: print "something"
....:
something
Jak wszyscy zauważyli użycie bloku else powoduje, że Twój kod jest bardziej czytelny i działa tylko wtedy, gdy wyjątek nie zostanie wyrzucony
In [14]: try:
dict_["b"]
except KeyError:
pass
else:
print "something"
....:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-24 00:53:17
Kiedy widzisz to:
try:
y = 1 / x
except ZeroDivisionError:
pass
else:
return y
Albo nawet to:
try:
return 1 / x
except ZeroDivisionError:
return None
Rozważ to zamiast:
import contextlib
with contextlib.suppress(ZeroDivisionError):
return 1 / x
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-13 23:40:49
To jest mój prosty fragment howto understanding try-except-else-finally block w Pythonie:
def div(a, b):
try:
a/b
except ZeroDivisionError:
print("Zero Division Error detected")
else:
print("No Zero Division Error")
finally:
print("Finally the division of %d/%d is done" % (a, b))
Spróbujmy div 1/1:
div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done
Spróbujmy div 1/0
div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-06 06:24:45
Zobacz poniższy przykład, który ilustruje wszystko o try-except-else-finally:
for i in range(3):
try:
y = 1 / i
except ZeroDivisionError:
print(f"\ti = {i}")
print("\tError report: ZeroDivisionError")
else:
print(f"\ti = {i}")
print(f"\tNo error report and y equals {y}")
finally:
print("Try block is run.")
Zaimplementuj go i przyjdź:
i = 0
Error report: ZeroDivisionError
Try block is run.
i = 1
No error report and y equals 1.0
Try block is run.
i = 2
No error report and y equals 0.5
Try block is run.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-25 05:04:02
OP, MASZ RACJĘ. else po try / except w Pythonie jest brzydki . prowadzi do innego obiektu kontrolującego przepływ, w którym nie ma potrzeby:
try:
x = blah()
except:
print "failed at blah()"
else:
print "just succeeded with blah"
Całkowicie jednoznacznym odpowiednikiem jest:
try:
x = blah()
print "just succeeded with blah"
except:
print "failed at blah()"
Jest to znacznie jaśniejsze niż klauzula else. Else po try / except nie jest często pisane, więc zajmuje chwilę, aby dowiedzieć się, jakie są implikacje.
To, że możesz coś zrobić, nie znaczy, że powinieneś coś zrobić.
Wiele funkcji zostało dodano do języków, bo ktoś pomyślał, że może się przydać. Problem w tym, że im więcej funkcji, tym mniej jasne i oczywiste są rzeczy, ponieważ ludzie zwykle nie używają tych dzwonków i gwizdków.
Tylko moje 5 centów. Muszę wyjść z tyłu i posprzątać dużo kodu napisanego przez programistów pierwszego roku studiów, którzy myślą, że są mądrzy i chcą pisać kod w jakiś Uber-tight, Uber-efficient sposób, kiedy to po prostu sprawia, że bałagan, aby spróbować przeczytać / zmodyfikować później. Głosuję za czytelność codziennie i dwa razy w niedziele.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-20 16:03:11