„Nie wiem, czy to z powodu ignorancji, ale nie podoba mi się tego rodzaju programowanie, ponieważ wykorzystuje wyjątki do kontroli przepływu”.
W świecie Python stosowanie wyjątków do kontroli przepływu jest powszechne i normalne.
Nawet programiści Python używają wyjątków do kontroli przepływu, a ten styl jest mocno upakowany w języku (tj. Protokół iteratora używa StopIteration do zakończenia pętli sygnału).
Ponadto zastosowano styl try-wyjątkiem, aby zapobiec warunkom wyścigowym związanym z niektórymi konstrukcjami „patrz przed skokiem” . Na przykład testowanie os.path.exists daje informacje, które mogą być nieaktualne do czasu ich użycia. Podobnie Queue.full zwraca informacje, które mogą być nieaktualne. W takich przypadkach styl try-else-else będzie generował bardziej niezawodny kod.
„Rozumiem, że wyjątki nie są błędami, powinny być stosowane tylko w wyjątkowych warunkach”
W niektórych innych językach zasada ta odzwierciedla ich normy kulturowe odzwierciedlone w ich bibliotekach. „Reguła” częściowo opiera się również na względach wydajnościowych dla tych języków.
Norma kulturowa Pythona jest nieco inna. W wielu przypadkach musisz użyć wyjątków dla kontroli przepływu. Ponadto użycie wyjątków w Pythonie nie spowalnia otaczającego kodu i wywoływania kodu, jak ma to miejsce w niektórych skompilowanych językach (tj. CPython już implementuje kod do sprawdzania wyjątków na każdym etapie, niezależnie od tego, czy faktycznie używasz wyjątków, czy nie).
Innymi słowy, twoje zrozumienie, że „wyjątki dotyczą wyjątków”, jest zasadą, która ma sens w niektórych innych językach, ale nie w Pythonie.
„Jeśli jednak jest ono zawarte w samym języku, musi istnieć dobry powód, prawda?”
Oprócz unikania warunków wyścigowych, wyjątki są również bardzo przydatne do wyciągania obsługi błędów poza pętlami. Jest to niezbędna optymalizacja w językach interpretowanych, które zwykle nie mają automatycznego ruchu niezmiennego w pętli .
Ponadto wyjątki mogą znacznie uprościć kod w typowych sytuacjach, w których zdolność do obsługi problemu jest daleka od miejsca powstania problemu. Na przykład powszechny jest kod wywołujący kod interfejsu użytkownika najwyższego poziomu dla logiki biznesowej, który z kolei wywołuje procedury niskiego poziomu. Sytuacje powstające w procedurach niskiego poziomu (takich jak zduplikowane rekordy unikatowych kluczy w dostępach do bazy danych) mogą być obsługiwane tylko w kodzie najwyższego poziomu (np. Proszenie użytkownika o nowy klucz, który nie powoduje konfliktu z istniejącymi kluczami). Zastosowanie wyjątków dla tego rodzaju przepływu sterowania pozwala procedurom średniego poziomu całkowicie zignorować problem i być ładnie oddzielonym od tego aspektu sterowania przepływem.
Jest tam fajny post na blogu o niezbędności wyjątków .
Zobacz także odpowiedź Przepełnienie stosu: czy wyjątki naprawdę dotyczą wyjątkowych błędów?
„Jaki jest powód istnienia try-else-else?”
Sama klauzula else jest interesująca. Działa, gdy nie ma wyjątku, ale przed klauzulą „w końcu”. To jest jego główny cel.
Bez klauzuli else jedyną opcją uruchomienia dodatkowego kodu przed finalizacją byłaby niezdarna praktyka dodawania kodu do klauzuli try. Jest to nieporadne, ponieważ grozi podniesieniem wyjątków w kodzie, które nie miały być chronione przez try-block.
Przypadek użycia uruchomienia 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. To jest dość rzadkie.
Innym przypadkiem użycia klauzuli else jest wykonywanie działań, które muszą wystąpić, gdy nie wystąpi żaden wyjątek i które nie występują, gdy obsługiwane są wyjątki. Na przykład:
recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')
Kolejny przykład występuje u niedowiarków:
try:
tests_run += 1
run_testcase(case)
except Exception:
tests_failed += 1
logging.exception('Failing test case: %r', case)
print('F', end='')
else:
logging.info('Successful test case: %r', case)
print('.', end='')
Wreszcie, najczęstszym zastosowaniem klauzuli else w bloku try jest upiększanie (wyrównanie wyników wyjątkowych i wyników innych niż wyjątkowe na tym samym poziomie wcięcia). Takie użycie jest zawsze opcjonalne i nie jest absolutnie konieczne.
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 wtry
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:
Pakiet „spróbuj, z wyjątkiem” zawiera dwie opcjonalne klauzule
else
ifinally
. Tak to jest właściwietry-except-else-finally
.else
oceni tylko wtedy, gdy nie ma wyjątku odtry
bloku. Pozwala nam uprościć bardziej skomplikowany kod poniżej: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:
Prawdą jest, że zamiast tego moglibyśmy umieścić kod w
else
bloku wtry
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 wtry
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 .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:
lub Python 3:
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ńczeniefor
pętli (StopIteration
). Oto hierarchia Pythona 3:Komentator zapytał:
Nie, nie
raise
zwrócisz wyjątku, po prostu ponownie go unieś, aby zachować ślad stosu.Lub, w Python 3, możesz zgłosić nowy wyjątek i zachować ślad za pomocą łączenia wyjątków:
Rozwijam tutaj swoją odpowiedź .
źródło
raise
lub zrobić łańcuch wyjątków - ale to więcej na temat i omówione tutaj: stackoverflow.com/q/2052390/541136 - prawdopodobnie usunę te komentarze po widziałem, że ich widziałeś.Python nie podziela poglądu, że wyjątków należy używać tylko w wyjątkowych przypadkach, w rzeczywistości idiomem jest „prosić o wybaczenie, a nie pozwolenie” . Oznacza to, że stosowanie wyjątków jako rutynowej części kontroli przepływu jest całkowicie dopuszczalne, a wręcz zalecane.
Zasadniczo jest to dobra rzecz, ponieważ działanie w ten sposób pomaga uniknąć pewnych problemów (jako oczywisty przykład często eliminuje się warunki wyścigu) i sprawia, że kod jest nieco bardziej czytelny.
Wyobraź sobie, że masz sytuację, w której pobierasz dane od użytkowników, które muszą zostać przetworzone, ale masz domyślną, która jest już przetworzona.
try: ... except: ... else: ...
Konstrukcja sprawia, że bardzo czytelnego kodu:Porównaj z tym, jak może działać w innych językach:
Zwróć uwagę na zalety. Nie ma potrzeby sprawdzania, czy wartość jest poprawna i parsowania jej osobno, są one wykonywane raz. Kod również postępuje bardziej logicznie, najpierw główna ścieżka do kodu, a po nim „jeśli to nie działa, zrób to”.
Ten przykład jest oczywiście nieco wymyślony, ale pokazuje, że istnieją przypadki dla tej struktury.
źródło
Odpowiedź na to jest zależna od kontekstu. Jeśli to zrobisz:
To pokazuje, że nie znasz dobrze Pythona. Ta funkcja jest zawarta w
dict.get
metodzie:try
/except
Blok jest dużo bardziej wizualnie bałagan i rozwlekły sposób pisania, co może być skutecznie wykonywany w jednej linii z metodą atomowej. Istnieją inne przypadki, w których jest to prawdą.Nie oznacza to jednak, że powinniśmy unikać wszelkiej obsługi wyjątków. W niektórych przypadkach preferowane jest unikanie warunków wyścigu. Nie sprawdzaj, czy plik istnieje, po prostu spróbuj go otworzyć i złap odpowiedni błąd IOError. Ze względu na prostotę i czytelność staraj się to kapsułkować lub rozróżnij jako apropos.
Przeczytaj Zen Pythona , rozumiejąc, że istnieją napięte zasady i uważaj na dogmaty, które zbyt mocno opierają się na którymkolwiek z zawartych w nim stwierdzeń.
źródło
Zobacz następujący przykład, który ilustruje wszystko o try-wyjątkiem-else-wreszcie:
Wdróż to i przyjdź:
źródło
Powinieneś być ostrożny z użyciem bloku w końcu, ponieważ nie jest to to samo, co użycie bloku else w próbie, z wyjątkiem. Ostatecznie blok zostanie uruchomiony niezależnie od wyniku próby, z wyjątkiem.
Jak wszyscy zauważyli, użycie bloku else powoduje, że kod jest bardziej czytelny i działa tylko wtedy, gdy wyjątek nie zostanie zgłoszony
źródło
Ilekroć to zobaczysz:
Lub nawet to:
Zastanów się nad tym:
źródło
Powiedziałbym, że tylko dlatego, że nikt inny nie opublikował tej opinii
W przeciwieństwie do słów kluczowych
try
,except
ifinally
znaczenieelse
zdania nie jest oczywiste; jest mniej czytelny. Ponieważ nie jest często używany, sprawi, że osoby czytające Twój kod będą chciały dokładnie sprawdzić dokumenty, aby upewnić się, że rozumieją, co się dzieje.(Piszę tę odpowiedź właśnie dlatego, że znalazłem
try/except/else
w mojej bazie kodu, co spowodowało chwilę wtf i zmusiło mnie do trochę googlowania).Gdziekolwiek widzę kod, taki jak przykład OP:
Wolę refaktoryzować
<1> wyraźny zwrot, wyraźnie pokazuje, że w wyjątkowym przypadku zakończyliśmy pracę
<2> jako miły drobny efekt uboczny, kod, który kiedyś był w
else
bloku, jest wydzielany z jednego poziomu.źródło
Oto mój prosty fragment opisujący, w jaki sposób zrozumieć blok try-oprócz-else-w końcu w Pythonie:
Spróbujmy div 1/1:
Spróbujmy div 1/0
źródło
OP, JESTEŚ PRAWIDŁOWY. Inna po wypróbowaniu / oprócz Pythona jest brzydka . prowadzi do innego obiektu kontroli przepływu, w którym żaden nie jest potrzebny:
Całkowicie wyraźnym odpowiednikiem jest:
Jest to o wiele wyraźniejsze niż klauzula else. Inna próba / wyjątek nie jest często zapisywana, więc zastanawianie się, jakie są tego konsekwencje, zajmuje chwilę.
To, że MOŻESZ coś zrobić, nie oznacza, że POWINIENEŚ coś zrobić.
Do języków dodano wiele funkcji, ponieważ 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 tutaj. Muszę odejść i wyczyścić dużo kodu napisanego przez programistów college'ów pierwszego roku, którzy myślą, że są inteligentni i chcą pisać kod w niesamowicie ciasny i efektywny sposób, kiedy to tylko robi bałagan spróbować później przeczytać / zmodyfikować. Głosuję za czytelnością każdego dnia i dwa razy w niedziele.
źródło
print
zdanie się nie powiedzie. Co się stanie, jeślix = blah()
zwróci astr
, ale wyciąg z rachunku jestprint 'just succeeded with blah. x == %d' % x
? Teraz maszTypeError
generację, w której nie jesteś przygotowany do obsługi jednego; sprawdzasz,x = blah()
aby znaleźć źródło wyjątku, którego nawet tam nie ma. Zrobiłem to (lub ekwiwalent) więcej niż raz, gdzieelse
powstrzymałoby mnie to od popełnienia tego błędu. Teraz wiem lepiej. :-Delse
Klauzula nie jest całkiem oświadczenie, i dopóki nie jesteś do tego przyzwyczajony, to nie jest intuicyjne. Ale też nie było,finally
kiedy po raz pierwszy zacząłem go używać ...else
klauzuli nie są wychwytywane przezexcept
.x = blah()
aby znaleźć źródło wyjątku”, atraceback
dlaczego miałbyś sprawdzać źródło wyjątku z niewłaściwego miejsca?