Czy powinienem zawsze określać typ wyjątku w instrukcjach „except”?

95

Podczas korzystania z PyCharm IDE użycie typu except:bez wyjątku wyzwala przypomnienie ze środowiska IDE, że ta klauzula wyjątku jest Too broad.

Czy powinienem ignorować tę radę? A może Pythonic zawsze określa typ wyjątku?

HorseloverFat
źródło
Jeśli chcesz dowiedzieć się więcej na ten temat niż odpowiedzi, Google połyka wyjątki. Oprócz nich możesz mieć wiele innych ciekawych nakazów i zakazów. Kod zapachy to kolejny.
Tony Hopkinson,

Odpowiedzi:

92

Prawie zawsze lepiej jest określić jawny typ wyjątku. Jeśli użyjesz except:klauzuli naked , możesz w końcu złapać wyjątki inne niż te, których się spodziewasz - może to ukryć błędy lub utrudnić debugowanie programów, które nie robią tego, czego się spodziewasz.

Na przykład, jeśli wstawiasz wiersz do bazy danych, możesz chcieć złapać wyjątek wskazujący, że wiersz już istnieje, aby można było przeprowadzić aktualizację.

try:
    insert(connection, data)
except:
    update(connection, data)

Jeśli określisz bare except:, złapiesz również błąd gniazda wskazujący, że serwer bazy danych przewrócił się. Najlepiej jest wychwytywać tylko wyjątki, które wiesz, jak sobie z nimi radzić - często lepiej jest, aby program zakończył się niepowodzeniem w momencie wystąpienia wyjątku, niż kontynuował, ale zachowywał się w dziwny, nieoczekiwany sposób.

Jeden przypadek, w którym możesz chcieć użyć bare, except:znajduje się na najwyższym poziomie programu, który musisz zawsze uruchomić, na przykład serwera sieciowego. Ale wtedy musisz bardzo uważać, aby rejestrować wyjątki, w przeciwnym razie niemożliwe będzie ustalenie, co się dzieje. Zasadniczo w programie powinno być co najwyżej jedno miejsce, które to robi.

Konsekwencją tego wszystkiego jest to, że twój kod nigdy nie powinien działać, raise Exception('some message')ponieważ zmusza kod klienta do użycia except:(lub except Exception:co jest prawie tak samo złe). Powinieneś zdefiniować wyjątek specyficzny dla problemu, który chcesz zasygnalizować (może dziedziczyć z jakiejś wbudowanej podklasy wyjątków, takiej jak ValueErrorlub TypeError). Lub powinieneś zgłosić konkretny wbudowany wyjątek. Dzięki temu użytkownicy Twojego kodu mogą ostrożnie wychwytywać tylko wyjątki, które chcą obsłużyć.

babbageclunk
źródło
7
+1 Bardzo prawdziwe. Jeszcze fajniej jest z przykładem: except:również łapie (między innymi) NameErrori AttributeError, więc jeśli źle przeliterujesz coś w trybloku (np. Twoja funkcja "wstaw" jest faktycznie wywoływana, insert_oneponieważ ktoś nie cenił spójności tak bardzo, jak powinien), zawsze po cichu próbuje update().
1
A co z sytuacją, gdy musisz upewnić się, że wyjątek nie zostanie wyrzucony nad bieżącą witryną wywołania? To znaczy - złapałem wszystkie specyficzne wyjątki, które mogę przewidzieć, teraz muszę dodać, że „jeśli coś, o czym nie myślałem, zostanie wyrzucone, muszę to zarejestrować, zanim zabije kontekst uruchomionego wykonywania” (na przykład main())?
Adam Parkin
Właśnie o takiej sytuacji mówię w akapicie rozpoczynającym się od „Jeden przypadek…”. Zdecydowanie jest to czasami potrzebne, ale każdy program powinien mieć tylko jedno miejsce, które to robi. I musisz uważać, aby w dzienniku było jasno widoczne, co się dzieje, w przeciwnym razie będziesz mieć frustrujący czas próbując zrozumieć, dlaczego Twój program nie obsługuje zdarzenia / żądania / czegokolwiek poprawnie.
babbageclunk
3
@delnan: Gorzej niż to. except Exception:złapie NameErrori AttributeErrorteż. Co sprawia, że gołą except:tak złe jest to, że rzeczy, które łapie ma żadnego interesu złowieniu, na przykład SystemExit(podniesione podczas rozmowy exitlub sys.exit, a teraz już zapobiec zamierzony exit) i KeyboardInterrupt(ponownie, jeśli przeboju użytkownika Ctrl-C, to prawdopodobnie nie chcą biec dalej tylko po to, by im złość). Tylko to ostatnie ma sens, aby złapać i powinno być złapane wyraźnie. Przynajmniej except Exception:pozwól tym dwóm rozmnażać się normalnie.
ShadowRanger
39

Nie należy ignorować rady udzielonej przez tłumacza.

Z przewodnika po stylu PEP-8 dla Pythona:

Podczas wychwytywania wyjątków należy wspomnieć o określonych wyjątkach, jeśli to możliwe, zamiast używać czystej klauzuli except :.

Na przykład użyj:

 try:
     import platform_specific_module 
 except ImportError:
     platform_specific_module = None 

Sama klauzula except: przechwytuje wyjątki SystemExit i KeyboardInterrupt, utrudniając przerwanie programu za pomocą Control-C i może maskować inne problemy. Jeśli chcesz wyłapać wszystkie wyjątki, które sygnalizują błędy programu, użyj wyjątku Exception: (bare except jest równoważne z wyjątkiem BaseException :).

Dobrą praktyczną zasadą jest ograniczenie używania samych klauzul „z wyjątkiem” do dwóch przypadków:

Jeśli program obsługi wyjątków będzie drukował lub logował śledzenie; przynajmniej użytkownik będzie świadomy wystąpienia błędu. Jeśli kod musi wykonać pewne prace porządkowe, ale pozwala na propagację wyjątku w górę za pomocą funkcji raise. spróbuj ... wreszcie może być lepszym sposobem załatwienia tej sprawy.

asheeshr
źródło
9

Nie dotyczy to Pythona.

Istotą wyjątków jest zajęcie się problemem jak najbliżej miejsca jego powstania.

Zachowujesz więc kod, który w wyjątkowych okolicznościach może wywołać problem i rozwiązanie „obok” siebie.

Chodzi o to, że nie możesz znać wszystkich wyjątków, które mogą zostać wyrzucone przez fragment kodu. Jedyne, co możesz wiedzieć, to to, że jeśli jest to, powiedzmy, wyjątek nie znaleziono pliku, możesz go przechwycić i poprosić użytkownika o uzyskanie takiego, który wykonuje lub anuluje tę funkcję.

Jeśli umieścisz spróbuj złapać, to bez względu na to, jaki problem wystąpił w twojej procedurze pliku (tylko do odczytu, uprawnienia, UAC, nie do końca pdf, itp.), Każdy z nich wpadnie do twojego pliku nie znaleziono catch, a twój użytkownik krzyczy „ale jest, ten kod jest gówniany”

Teraz jest kilka sytuacji, w których możesz złapać wszystko, ale należy je wybierać świadomie.

Są przechwytywane, cofają jakąś akcję lokalną (taką jak tworzenie lub blokowanie zasobu (na przykład otwieranie pliku na dysku w celu zapisu), a następnie ponownie zgłaszasz wyjątek, aby zająć się nim na wyższym poziomie)

Drugi ty nie obchodzi cię, dlaczego poszło źle. Na przykład drukowanie. Możesz mieć cały haczyk, aby powiedzieć, że jest jakiś problem z twoją drukarką, napraw go i nie zabijaj aplikacji z tego powodu. Podobnie na próżno, gdyby twój kod wykonywał serię oddzielnych zadań używając jakiegoś harmonogramu, nie chciałbyś, żeby cała rzecz umarła, ponieważ jedno z zadań się nie powiodło.

Uwaga Jeśli wykonasz powyższe, nie mogę polecić jakiegoś rodzaju rejestrowania wyjątków, np. Spróbuj złapać koniec dziennika, wystarczająco wysoko.

Tony Hopkinson
źródło
Powiedziałbym, że chodzi o równowagę. Musisz złapać wyjątek wystarczająco wcześnie, aby móc się z niego zregenerować i wystarczająco późno, aby wiedzieć, gdzie się z nim udać. Dlatego obsługa wyjątków w Javie powoduje takie spustoszenie, ponieważ musisz ponownie zawijać wyjątek na każdym kroku i tracisz informacje.
dhill
2
+1 za „nie obchodzi cię, dlaczego poszło źle”. Używam go w kilku miejscach wokół jednego wiersza kodu, w którym analizuję datę / godzinę z adresu URL. Biblioteka analizująca datę / godzinę innej firmy nie zawiera wszystkich wyjątków, które może zgłosić (oprócz standardowego ValueError znalazłem OverflowError i TypeError, ale prawdopodobnie jest ich więcej), a poza tym naprawdę nie obchodzi mnie dlaczego został zgłoszony wyjątek, chcę po prostu przekazać użytkownikowi rozsądny komunikat o błędzie, informujący, że coś jest nie tak z datą / godziną.
Michael Rodby,
3

Z tym też złapiesz np. Control-C, więc nie rób tego, chyba że ponownie nim „rzucisz”. Jednak w takim przypadku powinieneś raczej użyć „w końcu”.

Ulrich Eckhardt
źródło
3

Zawsze określić typ wyjątku, istnieje wiele rodzajów nie chcesz złapać, jak SyntaxError, KeyboardInterrupt, MemoryErroritp

Pavel Anossov
źródło
2
czy użycie except Exception:omija powyższe typy, których nie chcemy złapać?
HorseloverFat
except Exceptionjest w porządku.
Ulrich Eckhardt
4
@HorseloverFat: except Exceptionłapie SyntaxErrori MemoryErrorponieważ jest to ich klasa bazowa. KeyboardInterrupt, SystemExit(podniesione przez sys.exit()) nie są przechwytywane (są to bezpośrednie podklasy BaseException)
jfs
brzmi, jakby to nie było idealne - lepiej sprecyzować.
HorseloverFat
3

Oto miejsca, w których używam, z wyjątkiem bez typu

  1. szybkie i brudne prototypowanie

To jest główne zastosowanie w moim kodzie dla niezaznaczonych wyjątków

  1. funkcja main () najwyższego poziomu, w której rejestruję każdy niezłapany wyjątek

Zawsze to dodaję, aby kod produkcyjny nie wylewał śladów stosu

  1. między warstwami aplikacji

Mam na to dwa sposoby:

  • Pierwszy sposób: gdy warstwa wyższego poziomu wywołuje funkcję niższego poziomu, zawija wywołania wpisanymi wyjątkami, aby obsłużyć wyjątki „najwyższego” niższego poziomu. Ale dodaję ogólną instrukcję except, aby wykryć nieobsłużone wyjątki niższego poziomu w funkcjach niższego poziomu.

Wolę to w ten sposób, łatwiej jest mi wykryć, które wyjątki powinny zostać odpowiednio przechwycone: „widzę” problem lepiej, gdy wyjątek niższego poziomu jest rejestrowany przez wyższy poziom

  • Drugi sposób: każda funkcja najwyższego poziomu warstw niższego poziomu ma swój kod zawinięty w ogólny, z wyjątkiem tego, że przechwytuje wszystkie nieobsługiwane wyjątki na tej określonej warstwie.

Niektórzy współpracownicy wolą ten sposób, ponieważ zachowuje on wyjątki niższego poziomu w funkcjach niższego poziomu, do których „należą”.

nipil
źródło
-14

Spróbuj tego:

try:
    #code
except ValueError:
    pass

Odpowiedź dostałem z tego linku, czy ktoś jeszcze napotkał ten problem Sprawdź to

Mekanic
źródło