Jest to mniej pytanie o naturę pisania kaczek, a raczej o pozostawanie pythonem, jak sądzę.
Po pierwsze - w przypadku dykt, w szczególności gdy struktura dykt jest dość przewidywalna, a dany klucz nie jest zwykle obecny, ale czasami tak jest, najpierw myślę o dwóch podejściach:
if myKey in dict:
do_some_work(dict[myKey])
else:
pass
I oczywiście podejście Ye Olde „wybaczenie vs pozwolenie”.
try:
do_some_work(dict[myKey])
except KeyError:
pass
Jako czeladnik Python, wydaje mi się, że widzę, że ten drugi bardzo go preferuje, co wydaje mi się dziwne, ponieważ w Pythonie dokumenty try/excepts
wydają się preferowane, gdy występuje rzeczywisty błąd, a nie… um… brak sukcesu?
Jeśli okazjonalny dykta nie ma klucza w myDict i wiadomo, że nie zawsze będzie miał ten klucz, to czy próba / próba kontekstu wprowadza w błąd? To nie jest błąd programistyczny, to tylko fakt danych - ten dykta po prostu nie miał tego konkretnego klucza.
Wydaje się to szczególnie ważne, gdy spojrzysz na składnię try / wyjątek / else, która wydaje się bardzo przydatna, jeśli chodzi o upewnienie się, że try nie wykryje zbyt wielu błędów. Możesz zrobić coś takiego:
try:
foo += bar
except TypeError:
pass
else:
return some_more_work(foo)
Czy to nie doprowadzi do przełknięcia wszelkiego rodzaju dziwnych błędów, które prawdopodobnie są wynikiem złego kodu? Powyższy kod może po prostu uniemożliwić zauważenie, że próbujesz dodać, 2 + {}
i możesz nigdy nie zdawać sobie sprawy, że pewna część kodu poszła strasznie źle. Nie sugeruję, że powinniśmy sprawdzać wszystkie typy, dlatego jest to Python, a nie JavaScript - ale znowu w kontekście try / wyjątkiem wydaje się, że powinien złapać program robiąc coś, czego nie powinien, zamiast tego umożliwienia kontynuacji.
Zdaję sobie sprawę, że powyższy przykład jest czymś w rodzaju kłótni i rzeczywiście jest celowo zły. Ale biorąc pod uwagę pytonowe wyznanie wiary, better to ask forgiveness than permission
nie mogę przestać czuć, że rodzi się pytanie, gdzie linia na piasku faktycznie znajduje się między poprawnym zastosowaniem if / else vs try / wyjątek, w szczególności gdy wiesz, czego się spodziewać po dane, z którymi pracujesz.
Nie mówię tu nawet o problemach z szybkością ani o najlepszych praktykach, jestem po prostu trochę zdezorientowany przez postrzegany schemat Venna przypadków, w których wygląda na to, że może pójść w obie strony, ale ludzie mylą się po stronie próby / z wyjątkiem ponieważ „ktoś gdzieś powiedział, że to Python”. Czy wyciągnąłem błędne wnioski na temat zastosowania tej składni?
Odpowiedzi:
Zamiast tego użyj metody get :
... gdzie
default_value
jest zwracana wartość, jeślithe_key
nie jest wsome_dict
. Jeśli pominieszdefault_value
,None
jest zwracany, jeśli brakuje klucza.Ogólnie rzecz biorąc, w Pythonie ludzie wolą próbować / z wyjątkiem sprawdzania czegoś najpierw - zobacz wpis EAFP w glosariuszu . Zauważ, że wiele funkcji „test członkostwa” używa wyjątków za kulisami.
źródło
dict
działaniem opartym na tym, co zostało znalezione, wydaje się, że jestif myDict.get(a_key) is not None: do_work(myDict[a_key])
to bardziej wyraziste i kolejny przykład niejawnego patrzenia przed przeskakiwaniem - a ponadto jest to nieco mniej czytelne IMHO. Być może nie rozumiemdict.get(a_key, a_default)
we właściwym kontekście, ale wydaje się, że jest to prekursor do napisania „instrukcji non-switch-if if / elses” lub wartości magicznych. Podoba mi się to, co robi ta metoda, przyjrzę się temu głębiej.data = myDict.get(a_key, default)
i albodo_work
zrobisz właściwą rzecz, gdy otrzymaszdefault
(np.None
) Lub zrobiszif data: do_work(data)
. W obu przypadkach potrzebne jest tylko jedno wyszukiwanie. (Wif data is not None: ...
obu przypadkach może to być tylko jedno wyszukiwanie, a wtedy Twoja logika może przejąć kontrolę).try/except/else
śmieci i właśnie poprawiło IMHO czytelność mojego kodu. Nauczyłem się cennej lekcji, którą słyszałem wcześniej, ale zaniedbałem wzięcie sobie do serca: „Jeśli masz wrażenie, że piszesz za dużo kodu, przestań i spójrz na funkcje językowe”. Dzięki!!Czy brakujący klucz jest regułą czy wyjątkiem ?
Z pragmatycznego punktu widzenia rzucanie / łapanie jest znacznie wolniejsze niż sprawdzanie, czy klucz znajduje się w tabeli. Jeśli brakujący klucz jest częstym zjawiskiem - powinieneś użyć warunku.
Kolejną rzeczą na korzyść warunku jest klauzula else - o wiele łatwiej jest zrozumieć, kiedy którykolwiek blok zostanie wykonany, należy wziąć pod uwagę, że nawet ta sama klasa wyjątków może zostać wyrzucona z kilku instrukcji w bloku try.
Kod w klauzuli oprócz nie powinien być częścią normalnej wady (np. Jeśli klucza nie ma, dodaj go) - powinien obsługiwać błąd.
źródło
try
/catch
szybciej.W duchu bycia „pytonicznym”, a zwłaszcza pisaniem kaczek - try / wyjątkiem wydaje mi się krucha.
Wszystko, czego naprawdę chcesz, to coś z dostępem przypominającym dyktowanie, ale
__getitem__
możesz to zmienić, aby zrobić coś niezupełnie, czego oczekujesz. Na przykład przy użyciudefaultdict
ze standardowej biblioteki:Ponieważ domyślną wartością zwracaną jest Falsy, sprawdzanie dostępu w ten sposób będzie działać przez większość czasu w porządku. Wygląda na to, że kod jest również prostszy, i dlatego ja i moi współpracownicy robiliśmy to od miesięcy.
Niestety, kilka miesięcy później te dodatkowe klucze faktycznie spowodowały problemy i trudne do wyśledzenia błędy, ponieważ
0
stały się prawidłową wartością. A czasami byłoby użyćin
do sprawdzenia, czy istnieje klucz, dzięki czemu kod robić różne rzeczy, kiedy to powinna być identyczna.Powodem, dla którego sugeruję, że wygląda na zepsuty, jest to, że w duchu Pythona polegającego na pisaniu kaczek wszystko, czego powinieneś chcieć, to coś podobnego do dyktowania, i
defaultdict
pasuje idealnie do tego wymogu, podobnie jak każdy obiekt, z którego można by dziedziczyćdict
. Nie możesz zagwarantować, że osoba dzwoniąca nie zmieni jej implementacji później, więc powinieneś zrobić to z najmniejszymi efektami ubocznymi.Użyj pierwszej wersji
if myKey in mydict
.Zauważ też, że chodzi konkretnie o przykład w pytaniu. Wyznanie Pythona „Lepiej prosić o wybaczenie niż pozwolenie” ma w dużej mierze na celu zapewnienie, że faktycznie postępujesz właściwie, gdy nie dostaniesz tego, czego chcesz. Na przykład sprawdzenie, czy plik istnieje, a następnie próba odczytania go - co najmniej 3 rzeczy, które mogę wymyślić z góry głowy, mogą pójść nie tak:
Pierwszy, w którym możesz spróbować „poprosić o pozwolenie”, ale z uwagi na warunki wyścigowe niekoniecznie zawsze zadziała; drugi jest zbyt łatwy do zapomnienia, aby sprawdzić; a trzeciego nie można sprawdzić bez próby odczytania pliku. W każdym razie, co robisz po nie prawie na pewno będzie taka sama, więc w tym przykładzie, że jest lepiej „prosić o przebaczenie” za pomocą try / except.
źródło