Czy powinienem przetestować, czy if
coś jest prawidłowe, czy po prostu try
to zrobić i wyłapać wyjątek?
- Czy istnieje solidna dokumentacja mówiąca, że jeden sposób jest preferowany?
- Czy jest jeden sposób bardziej pytoniczny ?
Na przykład, czy powinienem:
if len(my_list) >= 4:
x = my_list[3]
else:
x = 'NO_ABC'
Lub:
try:
x = my_list[3]
except IndexError:
x = 'NO_ABC'
Kilka myśli ...
PEP 20 mówi:
Błędy nigdy nie powinny przejść bezgłośnie.
Chyba że wyraźnie uciszono.
Czy użycie try
zamiast an if
powinno być interpretowane jako cichy błąd? A jeśli tak, to czy wyraźnie go uciszasz, używając go w ten sposób, dzięki czemu jest OK?
Ja nie odnosząc się do sytuacji, w których można zrobić tylko rzeczy 1 drogę; na przykład:
try:
import foo
except ImportError:
import baz
if index in mylist
testy czy indeks jest elementem mylisty, a nie możliwym indeksem. Wolałbyśif index < len(mylist)
zamiast tego.if/else
try/catch
W tym konkretnym przypadku powinieneś użyć czegoś zupełnie innego:
Ogólnie jednak: jeśli spodziewasz się, że test często kończy się niepowodzeniem, użyj
if
. Jeśli test jest kosztowny w porównaniu z próbą wykonania operacji i przechwyceniem wyjątku, jeśli się nie powiedzie, użyjtry
. Jeśli żaden z tych warunków nie ma zastosowania, przejdź do łatwiejszego odczytu.źródło
Używanie
try
iexcept
bezpośrednio, zamiast wewnątrzif
strażnik powinien zawsze być wykonane, jeśli istnieje jakakolwiek możliwość wystąpienia sytuacji wyścigu. Na przykład, jeśli chcesz mieć pewność, że katalog istnieje, nie rób tego:Jeśli inny wątek lub proces utworzy katalog między
isdir
amkdir
, zakończysz pracę. Zamiast tego zrób to:To zakończy się tylko wtedy, gdy nie można utworzyć katalogu „foo”.
źródło
Jeśli sprawdzenie, czy coś nie powiedzie się, zanim to zrobisz, jest trywialne, prawdopodobnie powinieneś to faworyzować. W końcu tworzenie wyjątków (w tym związanych z nimi śledzenia) zajmuje trochę czasu.
Wyjątki należy stosować do:
break
nie prowadzi cię wystarczająco daleko) lub ...Zauważ, że często prawdziwa odpowiedź brzmi „nie” - na przykład w pierwszym przykładzie, co naprawdę powinieneś zrobić, to
.get()
podać wartość domyślną:źródło
if 'ABC' in myDict: x = myDict['ABC']; else: x = 'NO_ABC'
jest to faktycznie często szybsze niż używanieget
, niestety. Nie mówię, że to najważniejsze kryteria, ale należy być tego świadomym.if / else
itry / except
mogą mieć swoje miejsce nawet wtedy, gdy istnieją alternatywy specyficzne dla danego przypadku, ponieważ mają one różne charakterystyki wydajności..get()
jest wyszukiwanie atrybutów i narzut wywołań funkcji na poziomie Pythona; używanie słów kluczowych we wbudowanych w zasadzie prowadzi bezpośrednio do C. Nie sądzę, aby w najbliższym czasie było to zbyt szybsze. Jeśli chodzi oif
vs.try
, metoda read dict.get () zwraca wskaźnik, który zawiera informacje o wydajności. Stosunek trafień do chybień ma znaczenie (try
może być szybszy, jeśli klucz prawie zawsze istnieje), podobnie jak rozmiar słownika.Jak wspominają inne posty, zależy to od sytuacji. Istnieje kilka niebezpieczeństw związanych z używaniem try / z wyjątkiem wcześniejszego sprawdzania poprawności danych, szczególnie podczas używania ich w większych projektach.
np. załóżmy, że masz:
IndexError nie mówi nic o tym, czy wystąpił podczas próby pobrania elementu index_list czy my_list.
źródło
Używanie
try
to uznanie, że błąd może minąć, co jest przeciwieństwem cichego przejścia. Za pomocąexcept
powoduje, że w ogóle nie przechodzi.Używanie
try: except:
jest preferowane w przypadkach, gdyif: else:
logika jest bardziej skomplikowana. Proste jest lepsze niż złożone; złożone jest lepsze niż skomplikowane; i łatwiej jest prosić o przebaczenie niż o pozwolenie.To, o czym ostrzega „błędy nigdy nie przechodzą po cichu”, to przypadek, w którym kod może zgłosić wyjątek, o którym wiesz, i gdzie projekt dopuszcza taką możliwość, ale nie zaprojektowałeś go w sposób radzący sobie z wyjątkiem. Moim zdaniem jawne uciszanie błędu byłoby czymś w rodzaju
pass
plikuexcept
bloku, co powinno być wykonywane tylko ze zrozumieniem, że „nic nie robienie” jest w rzeczywistości poprawną obsługą błędu w danej sytuacji. (To jeden z nielicznych przypadków, w których wydaje mi się, że komentarz w dobrze napisanym kodzie jest prawdopodobnie naprawdę potrzebny).Jednak w twoim konkretnym przykładzie żadne z nich nie jest właściwe:
Powodem, dla którego wszyscy na to zwracają uwagę - nawet jeśli uznajesz swoje ogólne pragnienie zrozumienia i niezdolność do wymyślenia lepszego przykładu - jest to, że równoważne kroki poboczne faktycznie istnieją w wielu przypadkach, a ich szukanie jest pierwszy krok w rozwiązaniu problemu.
źródło
Ilekroć używasz
try/except
do sterowania przepływem, zadaj sobie pytanie:try
blok się powiedzie, a kiedy zawodzi?try
bloku?try
blok zgłasza wyjątek?try
bloku ulegnie zmianie, czy przepływ sterowania nadal będzie zachowywał się zgodnie z oczekiwaniami?Jeśli odpowiedź na jedno lub więcej z tych pytań brzmi „nie”, można prosić o dużo przebaczenia; najprawdopodobniej od przyszłego ja.
Przykład. Niedawno widziałem kod w większym projekcie, który wyglądał tak:
Rozmawiając z programistą okazało się, że zamierzony przepływ sterowania to:
To zadziałało, ponieważ
foo
wykonano zapytanie do bazy danych i zapytanie byłoby pomyślne, gdybyx
było liczbą całkowitą i wyrzuciłoProgrammingError
ifx
było listą.Używanie
try/except
jest tutaj złym wyborem:ProgrammingError
nie zdradza rzeczywistego problemu (x
nie jest to liczba całkowita), co utrudnia dostrzeżenie, co się dzieje.ProgrammingError
Jest podnoszona podczas połączenia bazy danych, która w czasie odpady. Sytuacja stałaby się naprawdę okropna, gdyby się okazało, żefoo
zapisuje coś do bazy danych, zanim zgłosi wyjątek lub zmieni stan innego systemu.ProgrammingError
jest zgłaszany tylko wtedy, gdyx
jest listą liczb całkowitych. Załóżmy na przykład, że wfoo
zapytaniu do bazy danych jest literówka . Może to również spowodować wzrostProgrammingError
. Konsekwencją jest to, żebar(x)
jest teraz wywoływana również kiedyx
jest liczbą całkowitą. Może to wywołać tajemnicze wyjątki lub spowodować nieprzewidziane skutki.try/except
Blok dodaje wymóg wszystkich implementacjach przyszłościfoo
. Za każdym razem, gdy się zmieniamyfoo
, musimy teraz pomyśleć o tym, jak obsługuje on listy i upewnić się, że zgłasza błąd,ProgrammingError
a nie, powiedzmy,AttributeError
żaden błąd.źródło
Dla ogólnego znaczenia możesz rozważyć przeczytanie idiomów i anty-idiomów w Pythonie: wyjątki .
W swoim konkretnym przypadku, jak powiedzieli inni, powinieneś użyć
dict.get()
:źródło