Chcę napisać funkcję w języku Python, która zwraca różne stałe wartości na podstawie wartości indeksu wejściowego.
W innych językach użyłbym instrukcji switch
lub case
, ale wydaje się, że Python nie ma switch
instrukcji. Jakie są zalecane rozwiązania Python w tym scenariuszu?
python
switch-statement
Michael Schneider
źródło
źródło
switch
jest w rzeczywistości bardziej „wszechstronny” niż coś zwracającego różne stałe wartości na podstawie wartości indeksu wejściowego. Pozwala na wykonanie różnych fragmentów kodu. W rzeczywistości nie musi nawet zwracać wartości. Zastanawiam się, czy niektóre z odpowiedzi tutaj są dobrym zamiennikiemswitch
instrukcji ogólnej , czy tylko w przypadku zwracania wartości bez możliwości wykonania ogólnych fragmentów kodu.Odpowiedzi:
Możesz użyć słownika:
źródło
get
metody byłoby prawdopodobnie bardziej normalne niż używaniecollections.defaultdict
w tym przypadku.}.get(x, default)
jeśli powinna być domyślna. (Uwaga: jest to o wiele ładniejsze niż to, co się stanie, jeśli nie wyłączysz domyślnej instrukcji zamiany!)Jeśli chcesz mieć wartości domyślne, możesz użyć
get(key[, default])
metody słownikowej :źródło
Zawsze lubiłem to robić w ten sposób
Stąd
źródło
.get()
(podobnie jak obecne najwyższe odpowiedzi) będą musiały z niecierpliwością oceniać wszystkie możliwości przed wysłaniem, a zatem nie tylko są (nie tylko bardzo, ale) niezwykle nieefektywne i nie mogą mieć skutków ubocznych; ta odpowiedź omija ten problem, ale jest bardziej szczegółowa. Użyłbym tylko if / elif / else, a nawet te zajmują tyle samo czasu, co pisanie „case”.[value]
, co zwróci tylko jedną z 3 funkcji (zakładając, żevalue
jest to jeden z 3 klawiszy). Funkcja nie została jeszcze wywołana w tym momencie. Następnie(x)
wywołuje właśnie zwróconą funkcję zx
argumentem as (a wynik przechodzi doresult
). Pozostałe 2 funkcje nie zostaną wywołane.Oprócz metod słownikowych (które naprawdę lubię BTW) możesz także użyć
if
-elif
- welse
celu uzyskania funkcjiswitch
/case
/default
:To oczywiście nie jest identyczne z przełącznikiem / skrzynką - nie możesz tak łatwo przejść przez to, co pomijanie
break
instrukcji, ale możesz mieć bardziej skomplikowany test. Jego formatowanie jest ładniejsze niż seria zagnieżdżonychif
s, nawet jeśli funkcjonalnie jest to do tego bliżej.źródło
get
sposobie, ale standardowy sposób jest po prostu bardziej czytelny.x = the.other.thing
wcześniej. Zwykle miałbyś jeden, wiele elifów i jeden inny, co jest łatwiejsze do zrozumienia.if/elif/else
?x in 'bc'
, należy pamiętać, że tak"" in "bc"
jestTrue
.Mój ulubiony przepis Pythona na przełącznik / skrzynkę to:
Krótkie i proste dla prostych scenariuszy.
Porównaj z ponad 11 liniami kodu C:
Możesz nawet przypisać wiele zmiennych za pomocą krotek:
źródło
default = -1; result = choices.get(key, default)
.result=key=='a'?1:key==b?2:-1
result = 1 if key == 'a' else (2 if key == 'b' else 'default')
. ale czy jedna linijka jest czytelna?Stosowanie:
Testy:
źródło
if
instrukcji?case(2)
blok wywołuje inną funkcję, która korzysta z switch (), to podczas wykonywaniacase(2, 3, 5, 7)
itp. W celu wyszukania następnego przypadku do wykonania użyje wartości przełączania ustawionej przez inną funkcję, a nie tej ustawionej przez bieżącą instrukcję przełączania .Mój ulubiony to naprawdę fajny przepis . Naprawdę ci się spodoba. Jest to najbliższy, jaki widziałem rzeczywiste instrukcje przypadków przełączania, szczególnie w funkcjach.
Oto przykład:
źródło
for case in switch()
zwith switch() as case
, ma większy sens, ponieważ trzeba s uruchomić tylko raz.with
to nie pozwalabreak
, więc opcja przewrotu jest usuwana.if c in set(range(0,9)): print "digit" elif c in set(map(chr, range(ord('a'), ord('z')))): print "lowercase"
?źródło
value
właściwości do klasy Switch, aby można było odwoływać sięcase.value
do instrukcji.Jest pewien wzór, którego nauczyłem się z kodu Twisted Python.
Możesz go użyć w dowolnym momencie, gdy potrzebujesz wysłać token i wykonać rozszerzony fragment kodu. W maszynie stanów masz
state_
metody i wysyłamy dalejself.state
. Przełącznik można rozszerzyć, dziedzicząc po klasie podstawowej i definiując własnedo_
metody. Często nawet nie maszdo_
metod w klasie bazowej.Edycja: jak dokładnie to jest używane
W przypadku SMTP otrzymasz
HELO
z drutu. Odpowiedni kod (ztwisted/mail/smtp.py
, zmodyfikowany w naszym przypadku) wygląda następującoOtrzymasz
' HELO foo.bar.com '
(lub możesz dostać'QUIT'
lub'RCPT TO: foo'
). Jest to symbolizowaneparts
jako['HELO', 'foo.bar.com']
. Rzeczywista nazwa wyszukiwania metody pochodzi zparts[0]
.(Oryginalna metoda jest również wywoływana
state_COMMAND
, ponieważ używa tego samego wzorca do implementacji automatu stanów, tj.getattr(self, 'state_' + self.mode)
)źródło
Powiedzmy, że nie chcesz po prostu zwracać wartości, ale chcesz użyć metod, które zmieniają coś na obiekcie. Przy zastosowaniu podanego tutaj podejścia byłoby:
To, co się tutaj dzieje, polega na tym, że Python ocenia wszystkie metody w słowniku. Więc nawet jeśli twoja wartość to „a”, obiekt zostanie zwiększony i zmniejszony o x.
Rozwiązanie:
Otrzymujesz więc listę zawierającą funkcję i jej argumenty. W ten sposób zwracany jest tylko wskaźnik funkcji i lista argumentów, a nie wartościowane. „wynik” następnie ocenia zwrócone wywołanie funkcji.
źródło
Po prostu wrzucę tutaj moje dwa centy. Powodem, dla którego nie ma instrukcji case / switch w Pythonie, jest to, że Python działa zgodnie z zasadą „Istnieje tylko jeden właściwy sposób na zrobienie czegoś”. Oczywiście można wymyślić różne sposoby odtwarzania funkcji przełącznika / wielkości liter, ale pythonicznym sposobem osiągnięcia tego jest konstrukcja if / elif. to znaczy
Po prostu czułem, że PEP 8 zasługuje na skinienie głową. Jedną z pięknych rzeczy w Pythonie jest jego prostota i elegancja. Wynika to w dużej mierze z zasad określonych w PEP 8, w tym „Istnieje tylko jeden właściwy sposób, aby coś zrobić”
źródło
rozwinięcie idei „dict as switch”. jeśli chcesz użyć domyślnej wartości dla przełącznika:
źródło
'default'
) od reguły (uzyskaj coś z tego słownika). Z założenia programy w języku Python używają wyjątków od razu. Biorąc to pod uwagę, użycieget
może potencjalnie uczynić kod nieco ładniejszym.Jeśli masz skomplikowany blok spraw, możesz rozważyć użycie tabeli wyszukiwania słownika funkcji ...
Jeśli nie zrobiłeś tego wcześniej, dobrze jest wejść do debuggera i zobaczyć dokładnie, jak słownik wyszukuje każdą funkcję.
Uwaga: nie używać „()” wewnątrz obudowy / słownik odnośnika albo zadzwoni do każdego ze swoich funkcji jako blok słownik / sprawa jest tworzony. Pamiętaj o tym, ponieważ chcesz wywołać każdą funkcję tylko raz, używając wyszukiwania w stylu skrótu.
źródło
Jeśli szukasz dodatkowej instrukcji, jako „switch”, zbudowałem moduł Pythona, który rozszerza Pythona. Nazywa się ESPY jako „Enhanced Structure for Python” i jest dostępny zarówno dla Python 2.x, jak i Python 3.x.
Na przykład w tym przypadku instrukcja switch może zostać wykonana za pomocą następującego kodu:
które mogą być użyte w ten sposób:
więc espy przetłumaczy to w Pythonie jako:
źródło
while True:
generowania kodu Pythona? To będzie nieuchronnie hitbreak
na dole wygenerowanego kodu Pythona, więc wydaje mi się, że zarównowhile True:
ibreak
może zostać usunięta. Ponadto, czy ESPY jest wystarczająco inteligentny, aby zmienić nazwęcont
jeśli użytkownik używa tej samej nazwy we własnym kodzie? W każdym razie chcę używać waniliowego Pythona, więc nie będę tego używał, ale mimo wszystko jest fajny. +1 za czysty chłód.while True:
ibreak
jest umożliwienie, ale nie wymaganie przewrotu .Odkryłem, że wspólna struktura przełączników:
można wyrazić w Pythonie w następujący sposób:
lub sformatowane w jaśniejszy sposób:
Zamiast być instrukcją, wersja Pythona jest wyrażeniem, którego wynikiem jest wartość.
źródło
parameter
p1==parameter
f = lambda x: 'a' if x==0 else 'b' if x==1 else 'c'
. Kiedy później zadzwoniłemf(2)
, dostałem'c'
;f(1)
,'b'
; if(0)
,'a'
. Co do p1 (x), oznacza predykat; tak długo, jak zwracaTrue
lubFalse
, niezależnie od tego, czy jest to wywołanie funkcji, czy wyrażenie, jest w porządku.Większość odpowiedzi tutaj jest dość stara, a zwłaszcza te zaakceptowane, więc warto je zaktualizować.
Po pierwsze, oficjalne FAQ Pythona obejmuje to i zaleca
elif
łańcuch dla prostych przypadków idict
dla większych lub bardziej złożonych przypadków. Sugeruje również zestawvisit_
metod (styl używany przez wiele frameworków serwerowych) w niektórych przypadkach:FAQ również wspomina PEP 275 , który został napisany, aby uzyskać oficjalną decyzję raz na zawsze o dodaniu instrukcji przełączania w stylu C. Ale ten PEP został odroczony do Pythona 3 i został oficjalnie odrzucony jako osobna propozycja, PEP 3103 . Odpowiedź brzmiała oczywiście nie - ale dwa PEP mają linki do dodatkowych informacji, jeśli interesują cię powody lub historia.
Jedną z rzeczy, które pojawiały się wiele razy (i można to zobaczyć w PEP 275, nawet jeśli zostało to wycięte jako rzeczywista rekomendacja) jest to, że jeśli naprawdę przeszkadza ci posiadanie 8 linii kodu do obsługi 4 przypadków, w porównaniu z 6 linie, które miałbyś w C lub Bash, zawsze możesz napisać to:
PEP 8 nie zachęca do tego, ale jest czytelny i niezbyt jednoznaczny.
Przez ponad dekadę od odrzucenia PEP 3103 kwestia instrukcji typu C lub nawet nieco bardziej rozbudowana wersja Go została uznana za martwą; ilekroć ktoś porusza to w python-pomysłach lub -dev, odnosi się do starej decyzji.
Jednak idea pełnego dopasowania wzorców w stylu ML pojawia się co kilka lat, zwłaszcza że takie języki, jak Swift i Rust ją przyjęły. Problem polega na tym, że trudno jest w dużym stopniu wykorzystać dopasowanie wzorca bez algebraicznych typów danych. Chociaż Guido był przychylny temu pomysłowi, nikt nie wpadł na propozycję, która bardzo dobrze pasuje do Pythona. (Możesz przeczytać mój strawman z 2014 r .). Może to ulec zmianie
dataclass
w wersji 3.7 i sporadycznych propozycjach dotyczących bardziej wydajnejenum
obsługi typów sum lub różnych propozycji dla różnych rodzajów powiązań lokalnych instrukcji (takich jak PEP 3150 lub zestaw propozycji obecnie dyskutowanych na żywo). Ale jak dotąd tak nie było.Od czasu do czasu pojawiają się również propozycje dopasowania w stylu Perla 6, które jest w zasadzie pomieszaniem wszystkiego, od
elif
wyrażeń regularnych po przełączanie typów pojedynczej wysyłki.źródło
Rozwiązanie do uruchamiania funkcji:
gdzie foo1 (), foo2 (), foo3 () i default () są funkcjami
źródło
foo2()
, tofoo1()
,foo3()
idefault()
funkcje są również zamiar uruchomić, czyli rzeczy może trwać długoget(option)()
. problem rozwiązany.W wyszukiwarce Google nie znalazłem prostej odpowiedzi, której szukałem. Ale i tak to wymyśliłem. To naprawdę bardzo proste. Postanowiłem go opublikować i być może zapobiec kilku mniejszym zadrapaniom na głowie innej osoby. Klucz jest po prostu „w” i krotkami. Oto zachowanie instrukcji przełączania z funkcją awaryjną, w tym awaryjną RANDOM.
Zapewnia:
źródło
break
na końcu kodu dlacase
. Ale myślę, że nie potrzebujemy takiego „przełomu” :)Rozwiązania, których używam:
Połączenie 2 opublikowanych tutaj rozwiązań, które jest stosunkowo łatwe do odczytania i obsługuje ustawienia domyślne.
gdzie
wyszukuje
"lambda x: x - 2"
w dykcie i używa gox=23
nie znajduje go w dykcie i używa domyślnego
"lambda x: x - 22"
zx=44
.źródło
źródło
źródło
Podobała mi się odpowiedź Marka Biesa
Ponieważ
x
zmienna musi być użyta dwukrotnie, zmodyfikowałem funkcje lambda do bez parametrów.Muszę uciekać
results[value](value)
Edycja: Zauważyłem, że mogę używać
None
pisma z słownikami. To by naśladowałoswitch ; case else
źródło
result[None]()
?result = {'a': 100, None:5000}; result[None]
None:
zachowuje się takdefault:
.Krótki i łatwy do odczytania, ma wartość domyślną i obsługuje wyrażenia zarówno w warunkach, jak i zwracanych wartości.
Jest jednak mniej wydajne niż rozwiązanie ze słownikiem. Na przykład Python musi przejrzeć wszystkie warunki przed zwróceniem wartości domyślnej.
źródło
możesz użyć wysłanego słownika:
Wynik:
źródło
Prosty, nie przetestowany; każdy warunek jest oceniany niezależnie: nie występuje awaria, ale wszystkie przypadki są oceniane (chociaż wyrażenie do włączenia jest oceniane tylko raz), chyba że istnieje instrukcja break. Na przykład,
druki
Was 1. Was 1 or 2. Was something.
(Cholera! Dlaczego nie mogę mieć końcowe spacje w inline bloków kodu?), jeśliexpression
ocenia się1
,Was 2.
jeśliexpression
ma wartość2
, lubWas something.
jeśliexpression
ma wartość czegoś innego.źródło
Definiowanie:
pozwala na użycie dość prostej składni, z przypadkami spakowanymi w mapę:
Próbowałem na nowo zdefiniować przełącznik w sposób, który pozwoliłby mi pozbyć się „lambda:”, ale poddałem się. Poprawianie definicji:
Pozwoliłem mi odwzorować wiele spraw na ten sam kod i podać domyślną opcję:
Każda replikowana sprawa musi znajdować się w swoim własnym słowniku; switch () konsoliduje słowniki przed wyszukaniem wartości. Wciąż jest brzydszy, niż bym chciał, ale ma podstawową efektywność używania haszowanego wyszukiwania wyrażenia, a nie pętli przez wszystkie klawisze.
źródło
Myślę, że najlepszym sposobem jest użycie idiomów języka Python, aby kod był testowalny . Jak pokazano w poprzednich odpowiedziach, używam słowników, aby korzystać ze struktur i języka Pythona i utrzymywać kod „case” w izolacji na różne sposoby. Poniżej znajduje się klasa, ale możesz użyć bezpośrednio modułu, globałów i funkcji. Klasa ma metody, które można przetestować w izolacji . W zależności od potrzeb możesz także grać metodami statycznymi i atrybutami.
Można skorzystać z tej metody, używając również klas jako kluczy tabeli „__choice_table”. W ten sposób można uniknąć nadużyć związanych z instancją i zachować wszystko w czystości i przetestować.
Załóżmy, że musisz przetworzyć wiele wiadomości lub pakietów z sieci lub Twojego MQ. Każdy pakiet ma własną strukturę i kod zarządzania (w sposób ogólny). Z powyższym kodem można zrobić coś takiego:
Tak więc złożoność nie rozkłada się w przepływie kodu, ale jest renderowana w strukturze kodu .
źródło
Rozwijanie odpowiedzi Grega Hewgilla - Możemy zamknąć słownik-rozwiązanie za pomocą dekoratora:
Może to być następnie użyte z
@case
-decoratoremDobrą wiadomością jest to, że zostało to już zrobione w module NeoPySwitch. Po prostu zainstaluj za pomocą pip:
źródło
Rozwiązaniem, z którego zwykle korzystam i które korzysta również ze słowników, jest:
Ma to tę zaletę, że nie próbuje za każdym razem oceniać funkcji, a Ty musisz tylko upewnić się, że funkcja zewnętrzna otrzymuje wszystkie informacje, których potrzebują funkcje wewnętrzne.
źródło
Do tej pory pojawiło się wiele odpowiedzi, które mówiły: „nie mamy przełącznika w Pythonie, zrób to w ten sposób”. Chciałbym jednak zauważyć, że sama instrukcja switch jest konstrukcją łatwą do nadużywania, której można i należy w większości przypadków unikać, ponieważ promują leniwe programowanie. Przykładem:
Teraz możesz to zrobić za pomocą instrukcji switch (jeśli Python ją oferuje), ale marnowałbyś swój czas, ponieważ istnieją metody, które robią to dobrze. A może masz coś mniej oczywistego:
Jednak tego rodzaju operacje można i należy obsługiwać za pomocą słownika, ponieważ będzie on szybszy, mniej złożony, mniej podatny na błędy i bardziej zwarty.
Zdecydowana większość „przypadków użycia” instrukcji zamiany przypada na jeden z tych dwóch przypadków; jest tylko bardzo mały powód, aby go użyć, jeśli dokładnie przemyślałeś swój problem.
Więc zamiast pytać „jak przełączyć się w Pythonie?”, Być może powinniśmy zapytać: „dlaczego chcę przełączyć się w Pythonie?”. ponieważ jest to często bardziej interesujące pytanie i często ujawnia wady w projektowaniu wszystkiego, co budujesz.
Nie oznacza to, że przełączników nigdy nie należy używać. Maszyny stanowe, leksery, parsery i automaty używają ich do pewnego stopnia i, ogólnie rzecz biorąc, kiedy zaczynasz od symetrycznego wejścia i przechodzisz do asymetrycznego wyjścia, mogą być przydatne; musisz tylko upewnić się, że nie używasz przełącznika jako młotka, ponieważ w kodzie widzisz gwoździe.
źródło