1717 Stimmen

Ersetzungen für switch-Anweisung in Python?

Ich möchte eine Funktion in Python schreiben, die je nach dem Wert eines Eingabeindexes verschiedene feste Werte zurückgibt.

In anderen Sprachen würde ich ein switch o case Anweisung, aber Python scheint nicht über eine switch Aussage. Was sind die empfohlenen Python-Lösungen in diesem Szenario?

77 Stimmen

PEP zum Thema, verfasst von Guido selbst: PEP 3103

28 Stimmen

@chb In diesem PEP erwähnt Guido nicht, dass if/elif-Ketten auch eine klassische Fehlerquelle sind. Es ist ein sehr anfälliges Konstrukt.

15 Stimmen

Was bei allen Lösungen fehlt, ist die Erkennung von doppelte Fallwerte . Als Fail-Fast-Prinzip kann dies ein größerer Verlust sein als die Leistung oder die Fallthrough-Funktion.

31voto

Lösung zum Ausführen von Funktionen:

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

wobei foo1(), foo2() und foo3() Funktionen sind

Beispiel 1 (mit Parametern):

option = number['type']
result = {
    'number':     value_of_int,  # result = value_of_int(number['value'])
    'text':       value_of_text, # result = value_of_text(number['value'])
    'binary':     value_of_bin,  # result = value_of_bin(number['value'])
}.get(option)(value['value'])

Beispiel 2 (keine Parameter):

option = number['type']
result = {
    'number':     func_for_number, # result = func_for_number()
    'text':       func_for_text,   # result = func_for_text()
    'binary':     func_for_bin,    # result = func_for_bin()
}.get(option)()

2 Stimmen

Ja, zum Beispiel wenn Ihre Variable option=="case2" Ihr Ergebnis=foo2()

0 Stimmen

Und so weiter und so fort.

0 Stimmen

Ja, ich verstehe den Zweck. Aber meine Sorge ist, dass, wenn Sie nur wollen foo2() die foo1() , foo3() y default() Alle Funktionen werden auch ausgeführt, was bedeutet, dass die Dinge lange dauern können.

30voto

GeeF Punkte 667

Angenommen, Sie wollen nicht nur einen Wert zurückgeben, sondern Methoden verwenden, die etwas an einem Objekt ändern. Die Verwendung des hier beschriebenen Ansatzes wäre:

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

Hier wertet Python alle Methoden des Wörterbuchs aus.

Auch wenn Ihr Wert 'a' ist, wird das Objekt inkrementiert. y dekrementiert um x.

Lösung:

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

result = func(*args)

Sie erhalten also eine Liste mit einer Funktion und ihren Argumenten. Auf diese Weise werden nur der Funktionszeiger und die Argumentenliste zurückgegeben, pas ausgewertet. result' wertet dann den zurückgegebenen Funktionsaufruf aus.

24voto

Asher Punkte 2458

Wenn Sie einen komplizierten Fallblock haben, können Sie die Verwendung einer Nachschlagetabelle im Funktionswörterbuch in Betracht ziehen...

Wenn Sie dies noch nicht getan haben, ist es eine gute Idee, in Ihren Debugger zu gehen und genau zu sehen, wie das Wörterbuch jede Funktion nachschlägt.

HINWEIS: Tun pas Verwenden Sie "()" innerhalb der Fall-/Wörterbuchsuche, da sonst jede Ihrer Funktionen aufgerufen wird, wenn der Wörterbuch-/Fallblock erstellt wird. Denken Sie daran, weil Sie jede Funktion nur einmal mit einem Hash-Stil Lookup aufrufen möchten.

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()

0 Stimmen

Ihre Lösung gefällt mir. Aber was, wenn ich nur einige Variablen oder Objekte übergeben muss?

0 Stimmen

Dies wird nicht funktionieren, wenn die Methode Parameter erwartet.

1 Stimmen

Dies ist die Methode, die offiziell in den Python-FAQ empfohlen wird

21voto

elp Punkte 201

Wenn Sie nach extra-statement, als "switch" suchen, habe ich ein Python-Modul gebaut, das Python erweitert. Es heißt ESPY als "Enhanced Structure for Python" und ist sowohl für Python 2.x als auch für Python 3.x verfügbar.

In diesem Fall könnte eine Switch-Anweisung beispielsweise durch den folgenden Code ausgeführt werden:

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

Das kann wie folgt verwendet werden:

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

So espy übersetzen es in Python als:

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

0 Stimmen

Sehr cool, aber was ist der Sinn der while True: am Anfang des generierten Python-Codes? Es wird unweigerlich auf die break am Ende des generierten Python-Codes, so dass es mir scheint, dass sowohl die while True: y break entfernt werden könnten. Ist der ESPY außerdem klug genug, den Namen des Wettbewerbs zu ändern? cont wenn der Benutzer denselben Namen in seinem eigenen Code verwendet? Auf jeden Fall möchte ich Vanilla Python verwenden, also werde ich dies nicht nutzen, aber es ist trotzdem cool. +1 für schiere Coolness.

0 Stimmen

@ArtOfWarfare Der Grund für die while True: y break s ist es, das Durchfallen zu erlauben, aber nicht zu verlangen.

0 Stimmen

Ist dieses Modul noch verfügbar?

19voto

abarnert Punkte 332066

Die meisten Antworten hier sind ziemlich alt, vor allem die akzeptierten, so dass es sich lohnt, sie zu aktualisieren.

Erstens, die offizielle Python-FAQ deckt dies ab und empfiehlt die elif Kette für einfache Fälle und die dict für größere oder komplexere Fälle. Sie schlägt auch eine Reihe von visit_ Methoden (ein Stil, der von vielen Server-Frameworks verwendet wird) für einige Fälle:

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

In den FAQ heißt es außerdem PEP 275 die geschrieben wurde, um eine offizielle Entscheidung über das Hinzufügen von Switch-Anweisungen im Stil von C zu treffen. Aber dieses PEP wurde eigentlich auf Python 3 verschoben, und es wurde nur offiziell als separater Vorschlag abgelehnt, PEP 3103 . Die Antwort war natürlich nein, aber die beiden PEPs enthalten Links zu zusätzlichen Informationen, falls Sie sich für die Gründe oder die Geschichte interessieren.


Eine Sache, die mehrfach zur Sprache kam (und in PEP 275 zu sehen ist, auch wenn sie als eigentliche Empfehlung herausgeschnitten wurde), ist, dass man, wenn es einen wirklich stört, 8 Zeilen Code zu haben, um 4 Fälle zu behandeln, im Gegensatz zu den 6 Zeilen, die man in C oder Bash hätte, immer dies schreiben kann:

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

Dies wird von PEP 8 nicht gerade gefördert, aber es ist lesbar und nicht zu unidiomatisch.


Seit mehr als einem Jahrzehnt, seit PEP 3103 abgelehnt wurde, gilt das Thema Case-Anweisungen im Stil von C oder sogar der etwas leistungsfähigeren Version in Go als tot; wann immer jemand es auf python-ideas oder -dev anspricht, wird er auf die alte Entscheidung verwiesen.

Die Idee eines vollständigen ML-ähnlichen Musterabgleichs kommt jedoch alle paar Jahre auf, vor allem seit Sprachen wie Swift und Rust sie übernommen haben. Das Problem ist, dass es schwierig ist, ohne algebraische Datentypen viel Nutzen aus dem Pattern Matching zu ziehen. Während Guido der Idee wohlwollend gegenübersteht, hat noch niemand einen Vorschlag gemacht, der gut in Python passt. (Sie können lesen mein Strohmann von 2014 für ein Beispiel). Dies könnte sich ändern mit dataclass in 3.7 und einige sporadische Vorschläge für ein leistungsfähigeres enum um Summentypen zu behandeln, oder mit verschiedenen Vorschlägen für verschiedene Arten von anweisungslokalen Bindungen (wie PEP 3150 oder die derzeit diskutierten Vorschläge auf -Ideen). Aber bis jetzt ist das nicht der Fall.

Gelegentlich gibt es auch Vorschläge für ein Matching im Stil von Perl 6, das im Grunde ein Mischmasch aus allem von elif zu regex zu single-dispatch type-switching.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X