Zamienniki dla instrukcji switch w Pythonie?

Chcę napisać funkcję w Pythonie, która zwraca różne stałe wartości na podstawie wartości indeksu wejściowego.

W innych językach używałbym instrukcji switch LUB case, ale Python nie wydaje się mieć instrukcji switch. Jakie są zalecane rozwiązania Pythona w tym scenariuszu?

Author: codeforester, 2008-09-13

30 answers

Przydałby się słownik:

def f(x):
    return {
        'a': 1,
        'b': 2,
    }[x]
 1184
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-13 00:38:24

Jeśli chcesz domyślnych można użyć słownika get(key[, default]) "metoda": {]}

def f(x):
    return {
        'a': 1,
        'b': 2
    }.get(x, 9)    # 9 is default if x not found
 1155
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-06 22:51:30

Zawsze lubiłem robić to w ten sposób

result = {
  'a': lambda x: x * 5,
  'b': lambda x: x + 7,
  'c': lambda x: x - 2
}[value](x)

Stąd

 312
Author: Mark Biek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-16 05:06:42

Oprócz metod słownikowych (które bardzo mi się podobają, BTW), możesz również użyć if-elif-else, aby uzyskać funkcjonalność switch/case / default:

if x == 'a':
    # Do the thing
elif x == 'b':
    # Do the other thing
if x in 'bc':
    # Fall-through by not using elif, but now the default case includes case 'a'!
elif x in 'xyz':
    # Do yet another thing
else:
    # Do the default

To oczywiście nie jest identyczne z switch/case - nie można mieć przewrócenia tak łatwo, jak pomijając przerwę; stwierdzenie, ale można mieć bardziej skomplikowany test. Jego formatowanie jest ładniejsze niż szereg zagnieżdżonych ifs, chociaż funkcjonalnie do tego jest bliższe.

 258
Author: Matthew Schinckel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-10 14:07:33

Mój ulubiony przepis Pythona na switch/case to:

choices = {'a': 1, 'b': 2}
result = choices.get(key, 'default')

Krótkie i proste dla prostych scenariuszy.

Porównaj z 11 + linijkami kodu C:

// C Language version of a simple 'switch/case'.
switch( key ) 
{
    case 'a' :
        result = 1;
        break;
    case 'b' :
        result = 2;
        break;
    default :
        result = -1;
}

Możesz nawet przypisać wiele zmiennych za pomocą krotek:

choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}
(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3'))
 134
Author: ChaimG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-16 21:58:26
class switch(object):
    value = None
    def __new__(class_, value):
        class_.value = value
        return True

def case(*args):
    return any((arg == switch.value for arg in args))

Użycie:

while switch(n):
    if case(0):
        print "You typed zero."
        break
    if case(1, 4, 9):
        print "n is a perfect square."
        break
    if case(2):
        print "n is an even number."
    if case(2, 3, 5, 7):
        print "n is a prime number."
        break
    if case(6, 8):
        print "n is an even number."
        break
    print "Only single-digit numbers are allowed."
    break

Testy:

n = 2
#Result:
#n is an even number.
#n is a prime number.
n = 11
#Result:
#Only single-digit numbers are allowed.
 87
Author: adamh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-31 14:24:52

Jest pewien wzorzec, którego nauczyłem się z pokręconego kodu Pythona.

class SMTP:
    def lookupMethod(self, command):
        return getattr(self, 'do_' + command.upper(), None)
    def do_HELO(self, rest):
        return 'Howdy ' + rest
    def do_QUIT(self, rest):
        return 'Bye'

SMTP().lookupMethod('HELO')('foo.bar.com') # => 'Howdy foo.bar.com'
SMTP().lookupMethod('QUIT')('') # => 'Bye'

Możesz go użyć w każdej chwili, gdy będziesz musiał wysłać token i wykonać Rozszerzony fragment kodu. W maszynie stanowej mielibyśmy metody state_, A wysyłanie na self.state. Ten przełącznik może być czysto rozszerzony poprzez dziedziczenie z klasy bazowej i definiowanie własnych metod do_. Często nie będziesz mieć nawet do_ metod w klasie bazowej.

Edit: jak dokładnie jest to używane

W przypadku SMTP otrzymasz HELO z przewodu. Odpowiedni kod (z twisted/mail/smtp.py, zmodyfikowany dla naszego przypadku) wygląda tak:

class SMTP:
    # ...

    def do_UNKNOWN(self, rest):
        raise NotImplementedError, 'received unknown command'

    def state_COMMAND(self, line):
        line = line.strip()
        parts = line.split(None, 1)
        if parts:
            method = self.lookupMethod(parts[0]) or self.do_UNKNOWN
            if len(parts) == 2:
                return method(parts[1])
            else:
                return method('')
        else:
            raise SyntaxError, 'bad syntax'

SMTP().state_COMMAND('   HELO   foo.bar.com  ') # => Howdy foo.bar.com

Otrzymasz ' HELO foo.bar.com ' (lub możesz otrzymać 'QUIT' lub 'RCPT TO: foo'). To jest tokenizowane do parts jako ['HELO', 'foo.bar.com']. Rzeczywista nazwa metody pochodzi z parts[0].

(oryginalna metoda jest również nazywana state_COMMAND, ponieważ używa tego samego wzorca do implementacji maszyny stanowej, tj. getattr(self, 'state_' + self.mode))

 42
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-13 17:42:03

Mój ulubiony to naprawdę fajny przepis . Spodoba ci się. Jest to najbliższy, jaki widziałem do rzeczywistych instrukcji switch case, szczególnie w funkcjach.

Oto przykład:

# The following example is pretty much the exact use-case of a dictionary,
# but is included for its simplicity. Note that you can include statements
# in each suite.
v = 'ten'
for case in switch(v):
    if case('one'):
        print 1
        break
    if case('two'):
        print 2
        break
    if case('ten'):
        print 10
        break
    if case('eleven'):
        print 11
        break
    if case(): # default, could also just omit condition or 'if True'
        print "something else!"
        # No need to break here, it'll stop anyway

# break is used here to look as much like the real thing as possible, but
# elif is generally just as good and more concise.

# Empty suites are considered syntax errors, so intentional fall-throughs
# should contain 'pass'
c = 'z'
for case in switch(c):
    if case('a'): pass # only necessary if the rest of the suite is empty
    if case('b'): pass
    # ...
    if case('y'): pass
    if case('z'):
        print "c is lowercase!"
        break
    if case('A'): pass
    # ...
    if case('Z'):
        print "c is uppercase!"
        break
    if case(): # default
        print "I dunno what c was!"

# As suggested by Pierre Quentel, you can even expand upon the
# functionality of the classic 'case' statement by matching multiple
# cases in a single shot. This greatly benefits operations such as the
# uppercase/lowercase example above:
import string
c = 'A'
for case in switch(c):
    if case(*string.lowercase): # note the * for unpacking as arguments
        print "c is lowercase!"
        break
    if case(*string.uppercase):
        print "c is uppercase!"
        break
    if case('!', '?', '.'): # normal argument passing style also applies
        print "c is a sentence terminator!"
        break
    if case(): # default
        print "I dunno what c was!"
 38
Author: John Doe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-07 06:12:47
class Switch:
    def __init__(self, value): self._val = value
    def __enter__(self): return self
    def __exit__(self, type, value, traceback): return False # Allows traceback to occur
    def __call__(self, *mconds): return self._val in mconds

from datetime import datetime
with Switch(datetime.today().weekday()) as case:
    if case(0):
        # Basic usage of switch
        print("I hate mondays so much.")
        # Note there is no break needed here
    elif case(1,2):
        # This switch also supports multiple conditions (in one line)
        print("When is the weekend going to be here?")
    elif case(3,4): print("The weekend is near.")
    else:
        # Default would occur here
        print("Let's go have fun!") # Didn't use case for example purposes
 30
Author: Ian Bell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-15 20:25:07

Powiedzmy, że nie chcesz po prostu zwracać wartości, ale chcesz użyć metod, które zmieniają coś na obiekcie. Używając podejścia podanego tutaj byłoby:

result = {
  'a': obj.increment(x),
  'b': obj.decrement(x)
}.get(value, obj.default(x))

Dzieje się tak, że python ocenia wszystkie metody w słowniku. Więc nawet jeśli twoja wartość to 'a', obiekt zostanie zwiększony i o x.

Rozwiązanie:

func, args = {
  'a' : (obj.increment, (x,)),
  'b' : (obj.decrement, (x,)),
}.get(value, (obj.default, (x,)))

result = func(*args)

Więc otrzymujesz listę zawierającą funkcję i jej argumenty. W ten sposób, tylko wskaźnik funkcji i lista argumentów get returned, not evaluated. 'result' następnie ocenia zwracane wywołanie funkcji.

 23
Author: GeeF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-30 08:31:55

Wrzucę tu moje 2 centy. Powodem, dla którego nie ma instrukcji case/switch w Pythonie, jest to, że Python przestrzega zasady "istnieje tylko jeden właściwy sposób, aby coś zrobić". Więc oczywiście można wymyślić różne sposoby odtworzenia funkcji switch/case, ale Pythonicznym sposobem osiągnięcia tego jest konstrukcja if / elif. ie

if something:
    return "first thing"
elif somethingelse:
    return "second thing"
elif yetanotherthing:
    return "third thing"
else:
    return "default thing"
Poczułem, że PEP 8 zasługuje na ukłon. Jedną z pięknych rzeczy w Pythonie jest jego prostota i elegancja. Że w dużej mierze opiera się na zasadach określonych w PEP 8, w tym "jest tylko jeden właściwy sposób, aby coś zrobić"
 15
Author: user2233949,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-14 22:19:38

Rozszerzenie idei "dict as switch". jeśli chcesz użyć domyślnej wartości dla przełącznika:

def f(x):
    try:
        return {
            'a': 1,
            'b': 2,
        }[x]
    except KeyError:
        return 'default'
 13
Author: Jeremy Cantrell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 15:37:15

Jeśli masz skomplikowany blok przypadków, możesz rozważyć użycie tabeli wyszukiwania słownika funkcji...

Jeśli nie zrobiłeś tego wcześniej, dobrym pomysłem jest wejście do debuggera i zobacz dokładnie, jak słownik wygląda każdej funkcji.

Uwaga: czy nie używaj "() " wewnątrz wyszukiwania case/dictionary lub wywoła każdą z twoich funkcji podczas tworzenia bloku dictionary/case. Zapamiętaj to, ponieważ chcesz wywołać każdą funkcję tylko raz za pomocą stylu hash / align = "left" /

def first_case():
    print "first"

def second_case():
    print "second"

def third_case():
    print "third"

mycase = {
'first': first_case, #do not use ()
'second': second_case, #do not use ()
'third': third_case #do not use ()
}
myfunc = mycase['first']
myfunc()
 13
Author: htmlfarmer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 21:52:45

Jeśli szukasz extra-statement, jako "switch", zbudowałem moduł Pythona, który rozszerza Pythona. Nazywa się ESPY jako "Enhanced Structure for Python" i jest dostępna zarówno dla Pythona 2.x i Python 3.x.

Na przykład w tym przypadku polecenie switch może być wykonane za pomocą następującego kodu:

macro switch(arg1):
    while True:
        cont=False
        val=%arg1%
        socket case(arg2):
            if val==%arg2% or cont:
                cont=True
                socket
        socket else:
            socket
        break

, które można użyć w następujący sposób:

a=3
switch(a):
    case(0):
        print("Zero")
    case(1):
        print("Smaller than 2"):
        break
    else:
        print ("greater than 1")

Więc ESPY Przetłumacz to w Pythonie jako:

a=3
while True:
    cont=False
    if a==0 or cont:
        cont=True
        print ("Zero")
    if a==1 or cont:
        cont=True
        print ("Smaller than 2")
        break
    print ("greater than 1")
    break
 13
Author: elp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-28 15:09:16

Nie znalazłem prostej odpowiedzi, której szukałem nigdzie w wyszukiwarce Google. Ale i tak to rozgryzłem. To bardzo proste. Postanowiłem go opublikować, a może zapobiec kilku zadrapaniom mniej na czyjejś głowie. Kluczem jest po prostu" in " i krotki. Oto zachowanie instrukcji switch z fall-through, w tym przypadkowe fall-through.

l = ['Dog', 'Cat', 'Bird', 'Bigfoot',
     'Dragonfly', 'Snake', 'Bat', 'Loch Ness Monster']

for x in l:
    if x in ('Dog', 'Cat'):
        x += " has four legs"
    elif x in ('Bat', 'Bird', 'Dragonfly'):
        x += " has wings."
    elif x in ('Snake',):
        x += " has a forked tongue."
    else:
        x += " is a big mystery by default."
    print(x)

print()

for x in range(10):
    if x in (0, 1):
        x = "Values 0 and 1 caught here."
    elif x in (2,):
        x = "Value 2 caught here."
    elif x in (3, 7, 8):
        x = "Values 3, 7, 8 caught here."
    elif x in (4, 6):
        x = "Values 4 and 6 caught here"
    else:
        x = "Values 5 and 9 caught in default."
    print(x)

Zapewnia:

Dog has four legs
Cat has four legs
Bird has wings.
Bigfoot is a big mystery by default.
Dragonfly has wings.
Snake has a forked tongue.
Bat has wings.
Loch Ness Monster is a big mystery by default.

Values 0 and 1 caught here.
Values 0 and 1 caught here.
Value 2 caught here.
Values 3, 7, 8 caught here.
Values 4 and 6 caught here
Values 5 and 9 caught in default.
Values 4 and 6 caught here
Values 3, 7, 8 caught here.
Values 3, 7, 8 caught here.
Values 5 and 9 caught in default.
 12
Author: JD Graham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-12 18:43:57

Znalazłem, że wspólna struktura przełącznika:

switch ...parameter...
case p1: v1; break;
case p2: v2; break;
default: v3;

Można wyrazić w Pythonie w następujący sposób:

(lambda x: v1 if p1(x) else v2 if p2(x) else v3)

Lub sformatowane w jaśniejszy sposób:

(lambda x:
     v1 if p1(x) else
     v2 if p2(x) else
     v3)

Zamiast instrukcji, wersja Pythona jest wyrażeniem, które ocenia do wartości.

 12
Author: leo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-29 17:25:57

Stosowane przeze mnie rozwiązania:

Kombinacja 2 z zamieszczonych tutaj rozwiązań, która jest stosunkowo łatwa do odczytania i obsługuje domyślne.

result = {
  'a': lambda x: x * 5,
  'b': lambda x: x + 7,
  'c': lambda x: x - 2
}.get(whatToUse, lambda x: x - 22)(value)

Gdzie

.get('c', lambda x: x - 22)(23)

Wyszukuje "lambda x: x - 2" w dict i używa go z x=23

.get('xxx', lambda x: x - 22)(44)

Nie znajduje go w dict i używa domyślnego "lambda x: x - 22" z x=44.

 10
Author: thomasf1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-23 16:27:48
# simple case alternative

some_value = 5.0

# this while loop block simulates a case block

# case
while True:

    # case 1
    if some_value > 5:
        print ('Greater than five')
        break

    # case 2
    if some_value == 5:
        print ('Equal to five')
        break

    # else case 3
    print ( 'Must be less than 5')
    break
 8
Author: user5224656,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-24 01:17:46

Zrobiłem (stosunkowo) elastyczne i wielokrotnego użytku rozwiązanie do tego. Można go znaleźć na GitHub jako Ten gist. Jeśli wynik funkcji przełącznika można wywołać, jest on wywoływany automatycznie.

 6
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-05 14:20:52

Spodobała mi się odpowiedź Marka Biesa

Ponieważ zmienna x musi być użyta dwa razy, zmodyfikowałem funkcje lambda NA bez parametru.

I have to run with results[value](value)

In [2]: result = {
    ...:   'a': lambda x: 'A',
    ...:   'b': lambda x: 'B',
    ...:   'c': lambda x: 'C'
    ...: }
    ...: result['a']('a')
    ...: 
Out[2]: 'A'

In [3]: result = {
    ...:   'a': lambda : 'A',
    ...:   'b': lambda : 'B',
    ...:   'c': lambda : 'C',
    ...:   None: lambda : 'Nothing else matters'

    ...: }
    ...: result['a']()
    ...: 
Out[3]: 'A'

Edit: zauważyłem, że mogę używać None type ze słownikami. Więc to emuluje switch ; case else

 6
Author: guneysus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:36

Większość odpowiedzi tutaj są dość stare, a zwłaszcza te zaakceptowane, więc wydaje się, że warto uaktualnić.

Po pierwsze, oficjalnyPython FAQ obejmuje to i zaleca łańcuch elif dla prostych przypadków i dict dla większych lub bardziej złożonych przypadków. Sugeruje również zestaw metod visit_ (styl używany przez wiele frameworków serwerowych) w niektórych przypadkach:

def dispatch(self, value):
    method_name = 'visit_' + str(value)
    method = getattr(self, method_name)
    method()

FAQ wspomina również PEP 275 , który został napisany, aby uzyskać oficjalną decyzję raz na zawsze na dodawanie instrukcji switch w stylu C. Jednak PEP został przeniesiony do Pythona 3 i został oficjalnie odrzucony jako osobna propozycja, PEP 3103. Odpowiedź brzmiała, oczywiście, nie-ale dwa PEPs mają linki do dodatkowych informacji, jeśli jesteś zainteresowany przyczynami lub historią.


Jedna rzecz, która pojawiła się wiele razy (i można zobaczyć w PEP 275, mimo że została wycięta jako prawdziwa rekomendacja) jest taka, że jeśli naprawdę przeszkadza Ci posiadanie 8 linijek kod do obsługi 4 przypadków, a 6 linii, które masz w C lub Bash, zawsze możesz napisać to:

if x == 1: print('first')
elif x == 2: print('second')
elif x == 3: print('third')
else: print('did not place')

Nie zachęca do tego PEP 8, ale jest czytelny i nie jest zbyt unidiomatyczny.


W ciągu ponad dziesięciu lat od odrzucenia PEP 3103, kwestia deklaracji przypadków w stylu C, a nawet nieco mocniejszej wersji w Go, została uznana za martwą; za każdym razem, gdy ktoś wspomina o tym na Pythonie-ideas 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 języki takie jak Swift i Rust przyjęły ją. Problem polega na tym, że trudno jest uzyskać wiele korzyści z dopasowania wzorców bez algebraicznych typów danych. Podczas gdy Guido sympatyzował z tym pomysłem, nikt nie wpadł na propozycję, która bardzo dobrze pasuje do Pythona. (Możesz przeczytać mój 2014 strawman dla przykładu.) Może się to zmienić wraz z dataclass w 3.7 i kilkoma sporadycznymi propozycjami mocniejszego enum do obsługi typy sum, lub z różnymi propozycjami dla różnych rodzajów oświadczeń-wiązań lokalnych (jak PEP 3150 , lub zestaw propozycji obecnie omawianych na-pomysły). Ale do tej pory tak nie było.

Istnieją również okazjonalnie propozycje dopasowania stylu Perla 6, co jest w zasadzie miszmasz wszystkiego, od elif przez regex po Przełączanie typu single-dispatch.

 6
Author: abarnert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-10 06:13:39
def f(x):
     return 1 if x == 'a' else\
            2 if x in 'bcd' else\
            0 #default

Krótki i łatwy do odczytania, ma wartość domyślną i obsługuje wyrażenia zarówno w warunkach, jak i wartościach zwrotnych.

Jest jednak mniej wydajny niż rozwiązanie ze słownikiem. Na przykład Python musi przeskanować wszystkie warunki przed zwróceniem wartości domyślnej.
 5
Author: emu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-05 20:05:12

Myślę, że najlepszym sposobem jest użycie idiomów języka python, aby Twój kod był testowany . Jak pokazano w poprzednich odpowiedziach, używam słowników, aby wykorzystać struktury Pythona i język i zachować kod "case" izolowany w różnych metodach. Poniżej znajduje się Klasa, ale możesz użyć bezpośrednio modułu, globali i funkcji. Klasa posiada metody, które mogą być testowane z izolacją . W zależności od potrzeb możesz bawić się statycznymi metodami i atrybutami też.

class ChoiceManager:

    def __init__(self):
        self.__choice_table = \
        {
            "CHOICE1" : self.my_func1,
            "CHOICE2" : self.my_func2,
        }

    def my_func1(self, data):
        pass

    def my_func2(self, data):
        pass

    def process(self, case, data):
        return self.__choice_table[case](data)

ChoiceManager().process("CHOICE1", my_data)

Można skorzystać z tej metody używając również klas jako kluczy z "__choice_table". W ten sposób można uniknąć nadużywania isinstance i zachować wszystko czyste i testowalne.

Przypuśćmy, że musisz przetworzyć wiele wiadomości lub pakietów z sieci lub Twojego MQ. Każdy pakiet ma swoją własną strukturę i swój kod zarządzania (w sposób ogólny). Z powyższym kodem można zrobić coś takiego:

class PacketManager:

    def __init__(self):
        self.__choice_table = \
        {
            ControlMessage : self.my_func1,
            DiagnosticMessage : self.my_func2,
        }

    def my_func1(self, data):
        # process the control message here
        pass

    def my_func2(self, data):
        # process the diagnostic message here
        pass

    def process(self, pkt):
        return self.__choice_table[pkt.__class__](pkt)

pkt = GetMyPacketFromNet()
PacketManager().process(pkt)


# isolated test or isolated usage example
def test_control_packet():
    p = ControlMessage()
    PacketManager().my_func1(p)

Więc złożoność nie jest spread w przepływie kodu, ale jest renderowany w strukturze kodu .

 4
Author: J_Zar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-20 14:54:43

Zrobiłem to małe i czyste rozwiązanie

result = {
    'case1':     foo1, 
    'case2':     foo2,
    'case3':     foo3,
    'default':   default,
}.get(option)()

Gdzie foo1(), foo2 (), foo3() i default () są funkcjami

 4
Author: Alejandro Quintanar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-17 05:10:40

Definiowanie:

def switch1(value, options):
  if value in options:
    options[value]()

Pozwala na użycie dość prostej składni, z przypadkami połączonymi w mapę:

def sample1(x):
  local = 'betty'
  switch1(x, {
    'a': lambda: print("hello"),
    'b': lambda: (
      print("goodbye," + local),
      print("!")),
    })

Próbowałem na nowo zdefiniować przełącznik w sposób, który pozwoliłby mi pozbyć się " lambda:", ale się poddałem. Podkręcanie definicji:

def switch(value, *maps):
  options = {}
  for m in maps:
    options.update(m)
  if value in options:
    options[value]()
  elif None in options:
    options[None]()

Pozwoliło mi odwzorować wiele przypadków na ten sam kod i podać domyślną opcję:

def sample(x):
  switch(x, {
    _: lambda: print("other") 
    for _ in 'cdef'
    }, {
    'a': lambda: print("hello"),
    'b': lambda: (
      print("goodbye,"),
      print("!")),
    None: lambda: print("I dunno")
    })

Każdy replikowany przypadek musi znajdować się w swoim własnym słowniku; switch() konsoliduje słowniki przed wyszukaniem wartość. Nadal jest brzydszy niż bym chciał, ale ma podstawową wydajność korzystania z haszowanego wyszukiwania na wyrażeniu, a nie pętli przez wszystkie klucze.

 3
Author: William H. Hooper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-25 18:23:33

Byłem trochę zdezorientowany po przeczytaniu odpowiedzi, ale to wszystko wyjaśniło:

def numbers_to_strings(argument):
    switcher = {
        0: "zero",
        1: "one",
        2: "two",
    }
    return switcher.get(argument, "nothing")

Ten kod jest analogiczny do:

function(argument){
    switch(argument) {
        case 0:
            return "zero";
        case 1:
            return "one";
        case 2:
            return "two";
        default:
            return "nothing";
    }
}

Sprawdź źródło , aby dowiedzieć się więcej o mapowaniu słownika do funkcji.

 3
Author: Yster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-19 11:49:28

Rozwiązaniem, którego używam, które również korzysta ze słowników, jest:

def decision_time( key, *args, **kwargs):
    def action1()
        """This function is a closure - and has access to all the arguments"""
        pass
    def action2()
        """This function is a closure - and has access to all the arguments"""
        pass
    def action3()
        """This function is a closure - and has access to all the arguments"""
        pass

   return {1:action1, 2:action2, 3:action3}.get(key,default)()

Ma to tę zaletę, że nie próbuje Oceniać funkcji za każdym razem, a ty po prostu musisz się upewnić, że funkcja zewnętrzna otrzymuje wszystkie informacje, których potrzebują funkcje wewnętrzne.

 3
Author: Tony Suffolk 66,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-06 16:45:58

Zainspirowany tą niesamowitą odpowiedzią . Nie wymaga zewnętrznego kodu. Nie testowane. Przewrócenie nie działa prawidłowo.

for case in [expression]:
    if case == 1:
        do_stuff()
        # Fall through

    # Doesn't fall through INTO the later cases
    if case in range(2, 5):
        do_other_stuff()
        break

    do_default()
 3
Author: Solomon Ucko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-26 21:44:57

Jeśli nie martwisz się utratą podświetlenia składni wewnątrz pakietu case, możesz wykonać następujące czynności:

exec {
    1: """
print ('one')
""", 
    2: """
print ('two')
""", 
    3: """
print ('three')
""",
}.get(value, """
print ('None')
""")

Gdzie value jest wartością. W C będzie to:

switch (value) {
    case 1:
        printf("one");
        break;
    case 2:
        printf("two");
        break;
    case 3:
        printf("three");
        break;
    default:
        printf("None");
        break;
}

Możemy również utworzyć funkcję pomocniczą w tym celu:

def switch(value, cases, default):
    exec cases.get(value, default)

Więc możemy użyć go w ten sposób dla przykładu z jednym, dwoma i trzema:

switch(value, {
    1: """
print ('one')
    """, 
    2: """
print ('two')
    """, 
    3: """
print ('three')
    """,
}, """
print ('None')
""")
 2
Author: DCPY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-25 22:56:30

Rozszerzając na odpowiedź Grega Hewgilla - możemy zamknąć słownik-rozwiązanie używając dekoratora:

def case(callable):
    """switch-case decorator"""
    class case_class(object):
        def __init__(self, *args, **kwargs):
            self.args = args
            self.kwargs = kwargs

        def do_call(self):
            return callable(*self.args, **self.kwargs)

return case_class

def switch(key, cases, default=None):
    """switch-statement"""
    ret = None
    try:
        ret = case[key].do_call()
    except KeyError:
        if default:
            ret = default.do_call()
    finally:
        return ret

To może być następnie używane z @case-dekorator

@case
def case_1(arg1):
    print 'case_1: ', arg1

@case
def case_2(arg1, arg2):
    print 'case_2'
    return arg1, arg2

@case
def default_case(arg1, arg2, arg3):
    print 'default_case: ', arg1, arg2, arg3

ret = switch(somearg, {
    1: case_1('somestring'),
    2: case_2(13, 42)
}, default_case(123, 'astring', 3.14))

print ret

Dobra wiadomość jest taka, że zostało to już zrobione w NeoPySwitch - module. Po prostu zainstaluj za pomocą pip:

pip install NeoPySwitch
 2
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:42