Czy istnieje uzasadnienie do zadecydować, który z try
lub if
konstrukcje do wykorzystania, podczas testowania zmienna ma wartość?
Na przykład istnieje funkcja, która zwraca listę lub nie zwraca wartości. Chcę sprawdzić wynik przed przetworzeniem. Która z poniższych byłaby lepsza i dlaczego?
result = function();
if (result):
for r in result:
#process items
lub
result = function();
try:
for r in result:
#process items
except TypeError:
pass;
Odpowiedzi:
Często słyszysz, że Python zachęca stylu EAFP („łatwiej jest prosić o przebaczenie niż o pozwolenie”) zamiast stylu LBYL („spójrz, zanim skoczysz ”). Dla mnie to kwestia wydajności i czytelności.
W twoim przykładzie (powiedz, że zamiast zwracać listę lub pusty ciąg, funkcja miała zwrócić listę lub
None
), jeśli spodziewasz się, że 99% czasuresult
faktycznie będzie zawierało coś iterowalnego, zastosowałbymtry/except
podejście. Będzie szybciej, jeśli wyjątki są naprawdę wyjątkowe. Jeśliresult
jest toNone
więcej niż 50% czasu, użycieif
jest prawdopodobnie lepsze.Aby wesprzeć to kilkoma pomiarami:
Tak więc, podczas gdy
if
wyciąg zawsze kosztuje, skonfigurowanietry/except
bloku jest prawie darmowe . Ale kiedyException
faktycznie nastąpi, koszt jest znacznie wyższy.Morał:
try/except
do sterowania przepływem,Exception
są naprawdę wyjątkowe.Z dokumentacji Pythona:
źródło
try/except
był o 25% szybszy niżif key in d:
w przypadkach, gdy klucz był w słowniku. Było znacznie wolniej, gdy klucza nie było w słowniku, zgodnie z oczekiwaniami i zgodnie z tą odpowiedzią.1/1
przypadku czasu nie jest dobrym wyborem, ponieważ zostaną one zoptymalizowane (zobaczdis.dis('1/1')
i zauważ, że nie następuje podział).Twoja funkcja nie powinna zwracać typów mieszanych (tj. Listy lub pustego łańcucha). Powinien zwrócić listę wartości lub tylko pustą listę. Wtedy nie musiałbyś niczego testować, tj. Twój kod zwija się do:
źródło
Proszę zignorować moje rozwiązanie, jeśli dostarczony przeze mnie kod nie jest na pierwszy rzut oka oczywisty i musisz przeczytać wyjaśnienie po przykładzie kodu.
Czy mogę założyć, że „brak zwróconej wartości” oznacza, że zwracana wartość to Brak? Jeśli tak, lub jeśli „brak wartości” jest wartością logiczną False, możesz wykonać następujące czynności, ponieważ Twój kod zasadniczo traktuje „brak wartości” jako „nie iteruj”:
Jeśli
function()
zwraca coś, co nie jest prawdziwe, iterujesz po pustej krotce, tj. Nie uruchamiasz żadnych iteracji. Zasadniczo jest to LBYL.źródło
Twój drugi przykład jest zepsuty - kod nigdy nie zgłosi wyjątku TypeError, ponieważ możesz iterować zarówno przez łańcuchy, jak i listy. Iterowanie po pustym łańcuchu lub liście jest również prawidłowe - spowoduje to zerowe wykonanie treści pętli.
źródło
W tym przypadku preferowana jest opcja Look Before You Leap. Z wyjątkiem podejścia TypeError może wystąpić w dowolnym miejscu w treści pętli i zostałby przechwycony i wyrzucony, co nie jest tym, czego chcesz i utrudnia debugowanie.
(Zgadzam się jednak z Brandonem Corfmanem: zwracanie Brak dla „brak elementów” zamiast pustej listy jest zepsute. Jest to nieprzyjemny zwyczaj programistów Java, którego nie powinno się widzieć w Pythonie. Lub w Javie).
źródło
Ogólnie odnoszę wrażenie, że wyjątki powinny być zarezerwowane dla wyjątkowych okoliczności. Jeśli
result
oczekuje się, że nigdy nie będzie pusty (ale może być, na przykład, gdy dysk się zawiesił itp.), Drugie podejście ma sens. Z drugiej strony, jeśli pustyresult
jest całkowicie rozsądny w normalnych warunkach, przetestuj go za pomocąif
instrukcji ma większy sens.Miałem na myśli (bardziej powszechny) scenariusz:
zamiast odpowiednika:
źródło
++
która nie działa w Pythonie, użyj+= 1
zamiast tego.bobince mądrze wskazuje, że zawijanie drugiej sprawy może również wychwycić TypeErrors w pętli, co nie jest tym, czego chcesz. Jeśli jednak naprawdę chcesz użyć try, możesz przetestować, czy jest iterowalny przed pętlą
Jak widać, jest raczej brzydki. Nie sugeruję tego, ale należy to wspomnieć dla kompletności.
źródło
None
zawsze sprawdzaj wynik za pomocąis None
lubis not None
. Z drugiej strony, zgłaszanie wyjątków dla uzasadnionych wyników jest również złym stylem. Wyjątki dotyczą nieoczekiwanych rzeczy. Przykład:str.find()
zwraca wartość -1, jeśli nic nie zostanie znalezione, ponieważ samo wyszukiwanie zakończyło się bez błędów.Jeśli chodzi o wydajność, użycie bloku try dla kodu, który normalnie nie generuje wyjątków, jest szybsze niż użycie instrukcji if za każdym razem. Tak więc decyzja zależy od prawdopodobieństwa wyjątkowych przypadków.
źródło
Zgodnie z ogólną zasadą, nigdy nie powinieneś używać try / catch ani żadnych rzeczy obsługujących wyjątki do kontrolowania przepływu. Mimo że iteracja za kulisami jest kontrolowana przez wywoływanie
StopIteration
wyjątków, nadal powinieneś preferować pierwszy fragment kodu od drugiego.źródło