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.

9voto

emu Punkte 1389
def f(x):
     return 1 if x == 'a' else\
            2 if x in 'bcd' else\
            0 #default

Sie ist kurz und leicht zu lesen, hat einen Standardwert und unterstützt Ausdrücke sowohl in Bedingungen als auch in Rückgabewerten.

Sie ist jedoch weniger effizient als die Lösung mit einem Wörterbuch. Python muss zum Beispiel alle Bedingungen durchsuchen, bevor es den Standardwert zurückgibt.

7voto

Woody1193 Punkte 3987

Bisher gab es viele Antworten, die sagten: "In Python gibt es keine Switch-Anweisung, machen Sie es auf diese Weise". Ich möchte jedoch darauf hinweisen, dass die switch-Anweisung selbst ein leicht zu missbrauchendes Konstrukt ist, das in den meisten Fällen vermieden werden kann und sollte, weil es träges Programmieren fördert. Ein typisches Beispiel:

def ToUpper(lcChar):
    if (lcChar == 'a' or lcChar == 'A'):
        return 'A'
    elif (lcChar == 'b' or lcChar == 'B'):
        return 'B'
    ...
    elif (lcChar == 'z' or lcChar == 'Z'):
        return 'Z'
    else:
        return None        # or something

Nun, Sie könnte dies mit einer switch-Anweisung tun (wenn Python eine anbietet), aber Sie würden Ihre Zeit verschwenden, weil es Methoden gibt, die dies einfach tun. Oder vielleicht haben Sie etwas weniger Offensichtliches:

def ConvertToReason(code):
    if (code == 200):
        return 'Okay'
    elif (code == 400):
        return 'Bad Request'
    elif (code == 404):
        return 'Not Found'
    else:
        return None

Diese Art von Operation kann und sollte jedoch mit einem Wörterbuch durchgeführt werden, da dies schneller, weniger komplex, weniger fehleranfällig und kompakter ist.

Und die überwiegende Mehrheit der "Anwendungsfälle" für switch-Anweisungen fällt in einen dieser beiden Fälle; es gibt nur sehr wenig Grund, eine davon zu verwenden, wenn Sie Ihr Problem gründlich durchdacht haben.

Anstatt also zu fragen, "wie schalte ich in Python um?", sollten wir vielleicht fragen, "warum will ich in Python umschalten?", denn das ist oft die interessantere Frage und wird oft Schwächen im Design von allem, was Sie bauen, aufdecken.

Das heißt aber nicht, dass Schalter nicht auch verwendet werden sollten. Zustandsautomaten, Lexer, Parser und Automaten verwenden sie alle bis zu einem gewissen Grad, und im Allgemeinen können sie nützlich sein, wenn man von einer symmetrischen Eingabe ausgeht und zu einer asymmetrischen Ausgabe gelangt; man muss nur darauf achten, dass man den Schalter nicht als Hammer benutzt, weil man einen Haufen Nägel in seinem Code sieht.

7voto

Tony Suffolk 66 Punkte 8648

Eine Lösung, die ich gerne verwende und bei der auch Wörterbücher zum Einsatz kommen, ist:

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

Dies hat den Vorteil, dass nicht jedes Mal versucht wird, die Funktionen zu evaluieren, und Sie müssen nur sicherstellen, dass die äußere Funktion alle Informationen erhält, die die inneren Funktionen benötigen.

6voto

J_Zar Punkte 1524

Ich denke, der beste Weg ist die Idiome der Python-Sprache nutzen, um Ihren Code testbar zu halten . Wie in früheren Antworten gezeigt, verwende ich Wörterbücher, um die Vorteile der Python-Strukturen und der Python-Sprache nutzen und halten Sie den "Fall"-Code in verschiedenen Methoden isoliert. Unten gibt es eine Klasse, aber Sie können direkt ein Modul, Globals und Funktionen verwenden. Die Klasse hat Methoden, die kann mit Isolierung getestet werden .

Je nach Ihren Bedürfnissen können Sie auch mit statischen Methoden und Attributen spielen.

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)

Es ist möglich die Vorteile dieser Methode nutzen, indem sie auch Klassen als Schlüssel verwenden von "__choice_table". Auf diese Weise können Sie vermeiden Instanzmissbrauch und alles sauber und testbar halten.

Angenommen, Sie müssen eine große Anzahl von Nachrichten oder Paketen aus dem Netz oder Ihrem MQ verarbeiten. Jedes Paket hat seine eigene Struktur und seinen eigenen Verwaltungscode (auf generische Weise).

Mit dem obigen Code ist es möglich, etwas wie dies zu tun:

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)

Also die Komplexität ist nicht im Codefluss verteilt, sondern wird in der Codestruktur wiedergegeben .

0 Stimmen

Wirklich hässlich... Schaltergehäuse ist so sauber beim Lesen. Kann nicht verstehen, warum es nicht in Python implementiert ist.

0 Stimmen

@AndyClifton: Entschuldigung... ein Beispiel? Denken Sie an jede Zeit, die Sie brauchen, um mehrere Entscheidungen Verzweigung Code haben und Sie können diese Methode anwenden.

0 Stimmen

@jmcollin92: Die switch-Anweisung ist bequem, da stimme ich zu. Allerdings neigt der Programmierer dazu, sehr lange Anweisungen und Code zu schreiben, der nicht wiederverwendbar ist. Die von mir beschriebene Methode ist sauberer zu testen und wiederverwendbar, IMHO.

6voto

Tom Punkte 375

Ausweitung der Die Antwort von Greg Hewgill - Wir können die Wörterbuchlösung mit einem Dekorator kapseln:

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

Diese kann dann mit dem @case -Dekorateur

@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

Die gute Nachricht ist, dass dies bereits geschehen ist in NeoPySwitch -Modul. Einfach mit pip installieren:

pip install NeoPySwitch

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