Mam metodę, która wywołuje 4 kolejne metody w celu sprawdzenia określonych warunków i zwraca natychmiast (nie sprawdzając kolejnych), ilekroć ktoś zwróci coś Prawdy.
def check_all_conditions():
x = check_size()
if x:
return x
x = check_color()
if x:
return x
x = check_tone()
if x:
return x
x = check_flavor()
if x:
return x
return None
To wygląda na dużo kodu bagażu. Zamiast każdej 2-liniowej instrukcji if wolę zrobić coś takiego:
x and return x
Ale to jest nieprawidłowy Python. Czy brakuje mi tutaj prostego, eleganckiego rozwiązania? Nawiasem mówiąc, w tej sytuacji te cztery metody sprawdzania mogą być drogie, więc nie chcę dzwonić do nich wiele razy.
python
if-statement
Bernard
źródło
źródło
x and return x
jest lepszy niżif x: return x
? Ten ostatni jest znacznie bardziej czytelny, a tym samym łatwy do utrzymania. Nie powinieneś zbytnio przejmować się liczbą znaków lub linii; liczy się czytelność. W każdym razie są to dokładnie te same znaki, które nie są spacjami, i jeśli naprawdę musisz,if x: return x
będą działały dobrze tylko w jednym wierszu.x
(w przeciwieństwie dobool(x)
), więc w obecnej postaci uważam, że można bezpiecznie założyć, że funkcje OP mogą zwrócić wszystko, a on chce najpierw wszystkiego, co jest zgodne z prawdą.Odpowiedzi:
Możesz użyć pętli:
Ma to tę dodatkową zaletę, że można teraz zmienić liczbę warunków.
Możesz użyć
map()
+filter()
(wersje Python 3, użyjfuture_builtins
wersji Python 2), aby uzyskać pierwszą taką pasującą wartość:ale jeśli jest to bardziej czytelne, jest dyskusyjne.
Inną opcją jest użycie wyrażenia generatora:
źródło
any
zamiast pętli.return any(condition() for condition in conditions)
any
ma prawie taką samą implementację w środku. Ale wygląda to znacznie lepiej, proszę opublikować jako odpowiedź)conditions
iarguments
? Jest to IMHO znacznie gorsze niż oryginalny kod, którego analiza zajmuje około 10 sekund.return next((check for check in checks if check), None)
.Alternatywnie do dobrej odpowiedzi Martijna, możesz połączyć
or
. Zwróci pierwszą prawdziwą wartość lubNone
jeśli nie ma prawdziwej wartości:Próbny:
źródło
\
aby umieścić każdy czek na osobnej linii.\
do przedłużania linii logicznej. Zamiast tego używaj nawiasów; więcreturn (....)
z nowymi liniami wstawionymi w razie potrzeby. Będzie to jednak jedna długa logiczna linia.Nie zmieniaj tego
Istnieją inne sposoby osiągnięcia tego, jak pokazują różne inne odpowiedzi. Żadne nie są tak jasne, jak oryginalny kod.
źródło
:
, ponieważ uważam, żeif x: return x
jest całkiem w porządku, a to sprawia, że funkcja wygląda na bardziej kompaktową. Ale to może być tylko ja.or
tak jak timgeb jest właściwym i dobrze rozumianym idiomem. Wiele języków ma to; być może kiedy jest nazywanyorelse
, jest jeszcze bardziej wyraźny, ale nawet zwykły staryor
(lub||
w innych językach) ma być rozumiany jako alternatywa do wypróbowania, jeśli pierwszy „nie działa”.or
etykiet „nie Pythonic” ani w żaden sposób zaciemniających, ale i tak jest to kwestia opinii --- tak jak powinno być.W rzeczywistości taka sama odpowiedź jak timgeb, ale można użyć nawiasu do lepszego formatowania:
źródło
Zgodnie z prawem Curly'ego możesz zwiększyć czytelność tego kodu, dzieląc dwie kwestie:
na dwie funkcje:
Pozwala to uniknąć:
... zachowując liniowy, łatwy do odczytania przepływ.
Prawdopodobnie możesz również wymyślić jeszcze lepsze nazwy funkcji, w zależności od konkretnej sytuacji, co czyni ją jeszcze bardziej czytelną.
źródło
return None
nie jest to konieczne, ponieważ funkcje zwracająNone
domyślnie. Jednak nie ma nic złego w powrocieNone
jawnym i podoba mi się, że zdecydowałeś się to zrobić.Jest to wariant pierwszego przykładu Martijns. Wykorzystuje również styl „zbieranie kalabrów”, aby umożliwić zwarcie.
Zamiast pętli możesz użyć wbudowanego
any
.Zwróć uwagę, że
any
zwraca wartość logiczną, więc jeśli potrzebujesz dokładnej wartości zwrotu czeku, to rozwiązanie nie będzie działać.any
nie rozróżnia14
,'red'
,'sharp'
,'spicy'
jako wartości zwracane, wszystkie będą zwracane jakoTrue
.źródło
next(itertools.ifilter(None, (c() for c in conditions)))
aby uzyskać rzeczywistą wartość bez rzutowania jej na wartość logiczną.any
faktycznie powoduje zwarcie?x = bar(); if x: return x;
Czy zastanawiałeś się nad pisaniem
if x: return x
wszystkiego w jednym wierszu?To nie jest mniej powtarzalne niż to, co miałeś, ale IMNSHO czyta dość płynniej.
źródło
Jestem dość zaskoczony, że nikt nie wspomniał o wbudowanym,
any
który został stworzony w tym celu:Zauważ, że chociaż ta implementacja jest prawdopodobnie najczystsza, ocenia wszystkie kontrole, nawet jeśli pierwsza jest
True
.Jeśli naprawdę musisz zatrzymać się przy pierwszym nieudanym sprawdzeniu, zastanów się nad użyciem tego,
reduce
co jest zrobione, aby przekonwertować listę do prostej wartości:W Twoim przypadku:
lambda a, f: a or f()
Jest to funkcja, która sprawdza, czy akumulatora
lub sprawdzić aktualnyf()
jestTrue
. Pamiętaj, że jeślia
takTrue
,f()
to nie będzie oceniane.checks
zawiera funkcje sprawdzania (f
pozycja od lambda)False
jest wartością początkową, w przeciwnym razie nie nastąpiłoby sprawdzenie i wynik byłby zawszeTrue
any
ireduce
są podstawowymi narzędziami do programowania funkcjonalnego. Gorąco zachęcam do ich trenowania, co równieżmap
jest niesamowite!źródło
any
działa tylko wtedy, gdy kontrole faktycznie zwracają wartość logiczną, dosłownieTrue
lubFalse
, ale pytanie tego nie określa. Musisz użyć,reduce
aby zwrócić rzeczywistą wartość zwróconą przez czek. Ponadto łatwo jest uniknąć oceny wszystkich kontroliany
za pomocą generatora, npany(c() for c in (check_size, check_color, check_tone, check_flavor))
. Jak w odpowiedzi Leonhardareduce
. Podobnie jak @DavidZ uważam, że twoje rozwiązanieany
powinno korzystać z generatora i należy zauważyć, że ogranicza się do zwrotuTrue
lubFalse
.any
działa z prawdziwymi wartościami:any([1, "abc", False]) == True
iany(["", 0]) == False
any
działa w tym celu tylko wtedy, gdy rzeczywiste wartości boolowskie są zwracane z funkcji kontrolnych.Jeśli chcesz mieć taką samą strukturę kodu, możesz użyć instrukcji potrójnych!
Myślę, że wygląda to ładnie i wyraźnie, jeśli na to spojrzysz.
Próbny:
źródło
x if x else <something>
można po prostu zmniejszyć dox or <something>
Dla mnie najlepsza odpowiedź to: @ phil-frost, a następnie @ wayne-werner.
Interesujące jest dla mnie to, że nikt nie powiedział nic o tym, że funkcja zwróci wiele różnych typów danych, co spowoduje, że wówczas będzie obowiązkowe sprawdzanie samego typu x, aby wykonać jakąkolwiek dalszą pracę.
Chciałbym więc połączyć odpowiedź @ PhilFrost z pomysłem zachowania jednego typu:
Zauważ, że
x
jest przekazywany jako argument, aleall_conditions
jest także używany jako przekazywany generator funkcji sprawdzających, w którym wszystkie z nichx
są sprawdzane i zwracająTrue
lubFalse
. Używającfunc
zall_conditions
jako wartością domyślną, możesz użyćassessed_x(x)
lub przekazać kolejny spersonalizowany generator za pośrednictwemfunc
.W ten sposób otrzymasz,
x
jak tylko jeden czek przejdzie, ale zawsze będzie tego samego typu.źródło
Idealnie, chciałbym ponownie napisać
check_
funkcje, aby powrócićTrue
lubFalse
zamiast wartości. Twoje czeki stają sięZakładając, że twoja
x
nie jest niezmienna, twoja funkcja może nadal ją modyfikować (chociaż nie mogą jej ponownie przypisać) - ale funkcja wywoływana icheck
tak nie powinna tak naprawdę modyfikować.źródło
Niewielka odmiana pierwszego przykładu Martijns powyżej, która pozwala uniknąć wewnątrz pętli:
źródło
Status or c()
pominie / zwróci krótkie oceny połączeń,c()
jeśliStatus
jest prawdą, więc kod w tej odpowiedzi nie wydaje się wywoływać więcej funkcji niż kod PO. stackoverflow.com/questions/2580136/…Lubię @ timgeb's. W międzyczasie chciałbym dodać, że wyrażanie
None
wreturn
instrukcji nie jest potrzebne, ponieważ zbióror
oddzielnych instrukcji jest oceniany, a pierwsze zero-zero, nic-puste, żadne-Brak jest zwracane, a jeśli nie, toNone
jest zwracane czy istniejeNone
czy nie!Więc moja
check_all_conditions()
funkcja wygląda następująco:Używając
timeit
znumber=10**7
, spojrzałem na czas działania wielu sugestii. Dla porównania użyłem tejrandom.random()
funkcji do zwrócenia ciągu znaków lubNone
na podstawie liczb losowych. Oto cały kod:A oto wyniki:
źródło
Ten sposób jest nieco poza pudełkiem, ale myślę, że wynik końcowy jest prosty, czytelny i ładnie wygląda.
Podstawową ideą jest
raise
wyjątek, gdy jedna z funkcji jest oceniana jako prawdziwa i zwraca wynik. Oto jak może to wyglądać:Potrzebujesz
assertFalsey
funkcji, która podnosi wyjątek, gdy jeden z wywoływanych argumentów funkcji jest oceniany jako prawdziwy:Powyższe można zmodyfikować, aby dostarczyć argumentów do oceny funkcji.
I oczywiście będziesz potrzebować
TruthyException
samego siebie. Ten wyjątek zapewnia wyjątek,object
który wywołał wyjątek:Oczywiście możesz zmienić oryginalną funkcję w coś bardziej ogólnego:
Może to być nieco wolniejsze, ponieważ używasz zarówno
if
instrukcji, jak i obsługi wyjątku. Jednak wyjątek jest obsługiwany maksymalnie jeden raz, więc wpływ na wydajność powinien być niewielki, chyba że spodziewasz się uruchomienia testu i uzyskaniaTrue
wartości wiele tysięcy razy.źródło
StopIteration
jest całkiem dobrym przykładem: wyjątek jest zgłaszany za każdym razem, gdy wyczerpujesz iterowalny. Rzeczą, której chcesz unikać, jest sukcesywne zgłaszanie wyjątków, które byłyby drogie. Ale zrobienie tego za jednym razem nie jest.Pythoniczny sposób albo wykorzystuje redukcję (jak ktoś już wspomniał) lub itertools (jak pokazano poniżej), ale wydaje mi się, że po prostu użycie zwarcia
or
operatora daje wyraźniejszy kodźródło
Wskoczę tutaj i nigdy nie napisałem ani jednej linii Pythona, ale zakładam, że
if x = check_something(): return x
jest poprawna?w takim razie:
źródło
if ( x := check_size() ) :
dla tego samego efektu.Lub użyj
max
:źródło
W przeszłości widziałem ciekawe implementacje instrukcji switch / case z dyktami, które doprowadziły mnie do tej odpowiedzi. Korzystając z podanego przez Ciebie przykładu, uzyskasz następujące informacje. (To szaleństwo
using_complete_sentences_for_function_names
, więccheck_all_conditions
przemianowano je nastatus
. Zobacz (1))Funkcja wyboru eliminuje potrzebę
check_FUNCTION
dwukrotnego wywoływania, tzn. Unikaszcheck_FUNCTION() if check_FUNCTION() else next
, dodając kolejną warstwę funkcji. Jest to przydatne w przypadku długo działających funkcji. Lambdas w nagraniu opóźnia wykonanie jego wartości aż do pętli while.Jako bonus można modyfikować kolejność wykonywania, a nawet pominąć niektóre badania przez przerabiania
k
is
npk='c',s={'c':'b','b':None}
zmniejsza liczbę testów i odwraca pierwotną kolejność przetwarzania.Ludzie
timeit
mogą się targować o koszt dodania dodatkowej warstwy lub dwóch do stosu i koszt dyktowania, ale wydajesz się bardziej zaniepokojony ładnością kodu.Alternatywnie prostsza implementacja może wyglądać następująco:
źródło
check_no/some/even/prime/every_third/fancy_conditions
ale tylko ta jedna funkcja, więc dlaczego nie zadzwonićstatus
lub jeśli ktoś nalegacheck_status
. Używanie_all_
jest zbyteczne, nie zapewnia integralności wszechświatów. Nazewnictwo powinno z pewnością używać spójnego zestawu słów kluczowych, wykorzystując odstępy między nazwami, jeśli to możliwe. Długie zdania najlepiej służą jako dokumenty. Rzadko potrzeba więcej niż 8-10 znaków, aby opisać coś zwięźle.check_all_conditions
to zła nazwa, ponieważ nie sprawdza wszystkich warunków, jeśli jest to prawda. Użyłbym czegoś takiegomatches_any_condition
.