Vervangingen voor switch-instructie in Python?

Ik wil een functie in Python schrijven die verschillende vaste waarden retourneert op basis van de waarde van een invoerindex.

In andere talen zou ik een switchof casestatement gebruiken, maar Python lijkt geen switchstatement te hebben. Wat zijn de aanbevolen Python-oplossingen in dit scenario?


Antwoord 1, autoriteit 100%

Het oorspronkelijke antwoord hieronder is geschreven in 2008. Sindsdien introduceerde Python 3.10 (2021) de matchcasestatement die een eersteklas implementatie van een “switch” voor Python biedt. Bijvoorbeeld:

def f(x):
    match x:
        case 'a':
            return 1
        case 'b':
            return 2

De matchcase-instructie is aanzienlijk krachtiger dan dit eenvoudige voorbeeld.


Je zou een woordenboek kunnen gebruiken:

def f(x):
    return {
        'a': 1,
        'b': 2,
    }[x]

Antwoord 2, autoriteit 86%

Als je van standaardinstellingen houdt, kun je het woordenboek get(key[, default])methode:

def f(x):
    return {
        'a': 1,
        'b': 2
    }.get(x, 9)    # 9 is default if x is not found

Antwoord 3, autoriteit 26%

Ik heb het altijd leuk gevonden om het op deze manier te doen

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

Vanaf hier


Antwoord 4, autoriteit 24%

Naast de woordenboekmethoden (die ik erg leuk vind, trouwens), kun je ook ifelifelsegebruiken om de switch/case/defaultfunctionaliteit:

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

Dit is natuurlijk niet identiek aan switch/case – je kunt niet zo gemakkelijk doorvallen als het weglaten van de break-instructie, maar je kunt een meer gecompliceerde test doen. De opmaak is mooier dan een reeks geneste ifs, hoewel het functioneel gezien het dichtst in de buurt komt.


Antwoord 5, autoriteit 16%

Python >= 3.10(pre-release)

Wauw, Python krijgt nu een echte match/casesyntaxis!

PEP 634, ‘structurele patroonovereenkomst’; a.k.a. switch/case, is goedgekeurd en toegevoegd aan Python 3.10.

Voorbeeld van gebruik:

match something:
    case 0 | 1 | 2:
        # Matches 0, 1 or 2
        print("Small number")
    case [] | [_]:
        # Matches an empty or single value sequence
        # Matches lists and tuples but not sets
        print("A short sequence")
    case str() | bytes():
        # Something of `str` or `bytes` type
        print("Something string-like")
    case _:
        # Anything not matched by the above
        print("Something else")

Python <= 3,9

Mijn favoriete Python-recept voor switch/case was:

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

Kort en eenvoudig voor eenvoudige scenario’s.

Vergelijk met 11+ regels C-code:

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

Je kunt zelfs meerdere variabelen toewijzen door tupels te gebruiken:

choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}
(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3'))

Antwoord 6, autoriteit 6%

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

Gebruik:

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

Testen:

n = 2
#Result:
#n is an even number.
#n is a prime number.
n = 11
#Result:
#Only single-digit numbers are allowed.

Antwoord 7, autoriteit 4%

Mijn favoriet is een heel mooi recept. Het komt het dichtst in de buurt van echte switch-case-statements, vooral in functies.

class switch(object):
    def __init__(self, value):
        self.value = value
        self.fall = False
    def __iter__(self):
        """Return the match method once, then stop"""
        yield self.match
        raise StopIteration
    def match(self, *args):
        """Indicate whether or not to enter a case suite"""
        if self.fall or not args:
            return True
        elif self.value in args: # changed for v1.5, see below
            self.fall = True
            return True
        else:
            return False

Hier is een voorbeeld:

# 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!"

Enkele van de opmerkingen gingen aan dat een oplossing voor contextbeheerder met with foo as casein plaats van for case in fookan schoner zijn, en voor grote schakelschriften dan kwadratisch gedrag kan een leuke touch zijn. Een deel van de waarde in dit antwoord met een voor lus is het vermogen om pauzes en gedaald te hebben, en als we bereid zijn om met onze keuze van zoekwoorden een klein beetje te spelen, kunnen we dat ook in een contextmanager krijgen:

class Switch:
    def __init__(self, value):
        self.value = value
        self._entered = False
        self._broken = False
        self._prev = None
    def __enter__(self):
        return self
    def __exit__(self, type, value, traceback):
        return False # Allows a traceback to occur
    def __call__(self, *values):
        if self._broken:
            return False
        if not self._entered:
            if values and self.value not in values:
                return False
            self._entered, self._prev = True, values
            return True
        if self._prev is None:
            self._prev = values
            return True
        if self._prev != values:
            self._broken = True
            return False
        if self._prev == values:
            self._prev = None
            return False
    @property
    def default(self):
        return self()

Hier is een voorbeeld:

# Prints 'bar' then 'baz'.
with Switch(2) as case:
    while case(0):
        print('foo')
    while case(1, 2, 3):
        print('bar')
    while case(4, 5):
        print('baz')
        break
    while case.default:
        print('default')
        break

Antwoord 8, Autoriteit 4%

class Switch:
    def __init__(self, value):
        self.value = value
    def __enter__(self):
        return self
    def __exit__(self, type, value, traceback):
        return False # Allows a traceback to occur
    def __call__(self, *values):
        return self.value in values
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

Antwoord 9, autoriteit 3%

Er is een patroon dat ik heb geleerd van Twisted Python-code.

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'

Je kunt het gebruiken wanneer je een token wilt verzenden en een uitgebreid stuk code wilt uitvoeren. In een state-machine zou je state_-methoden hebben en verzending op self.state. Deze schakeloptie kan netjes worden uitgebreid door over te nemen van de basisklasse en uw eigen do_-methoden te definiëren. Vaak heb je niet eens do_methoden in de basisklasse.

Bewerken: hoe wordt dat precies gebruikt

In het geval van SMTP ontvangt u HELOvan de draad. De relevante code (van twisted/mail/smtp.py, aangepast voor ons geval) ziet er als volgt uit

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

Je ontvangt ' HELO foo.bar.com '(of misschien krijg je 'QUIT'of 'RCPT TO: foo'). Dit is tokenized in partsals ['HELO', 'foo.bar.com']. De daadwerkelijke naam voor het opzoeken van de methode is afkomstig uit parts[0].

(De originele methode wordt ook state_COMMANDgenoemd, omdat het hetzelfde patroon gebruikt om een toestandsmachine te implementeren, namelijk getattr(self, 'state_' + self.mode))


Antwoord 10, autoriteit 2%

Stel dat u niet alleen een waarde wilt retourneren, maar methoden wilt gebruiken die iets aan een object veranderen. Het gebruik van de hier vermelde aanpak zou zijn:

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

Hier evalueert Python alle methoden in het woordenboek.

Dus zelfs als uw waarde ‘a’ is, wordt het object verhoogd enverlaagd met x.

Oplossing:

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

Je krijgt dus een lijst met een functie en zijn argumenten. Op deze manier worden alleen de functieaanwijzer en de argumentenlijst geretourneerd, nietgeëvalueerd. ‘result’ evalueert vervolgens de geretourneerde functieaanroep.


Antwoord 11

Ik ga hier even mijn twee cent inleveren. De reden dat er geen case/switch-statement in Python is, is omdat Python het principe volgt van “er is maar één juiste manier om iets te doen”. Het is dus duidelijk dat je verschillende manieren kunt bedenken om switch/case-functionaliteit te recreëren, maar de Pythonische manier om dit te bereiken is de if/elif-constructie. D.w.z.

if something:
    return "first thing"
elif somethingelse:
    return "second thing"
elif yetanotherthing:
    return "third thing"
else:
    return "default thing"

Ik vond dat PEP 8hier een knipoog verdiende. Een van de mooie dingen van Python is de eenvoud en elegantie. Dat is grotendeels afgeleid van principes die zijn uiteengezet in PEP 8, waaronder “Er is maar één juiste manier om iets te doen.”


Antwoord 12

Oplossing om functies uit te voeren:

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

waar foo1(), foo2() en foo3() functies zijn

Voorbeeld 1(met parameters):

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'])

Voorbeeld 2(geen parameters):

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

Antwoord 13

Als je extra-statement zoekt, als “switch”, heb ik een Python-module gebouwd die Python uitbreidt. Het heet ESPYals “Enhanced Structure for Python” en is beschikbaar voor zowel Python 2.x als Python 3.x.

In dit geval kan bijvoorbeeld een switch-instructie worden uitgevoerd met de volgende code:

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

Dat kan als volgt worden gebruikt:

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

Dus espy vertaal het 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

Antwoord 14

Als je een ingewikkeld blok met hoofdletters hebt, kun je overwegen een functiewoordenboek-opzoektabel te gebruiken…

Als je dit nog niet eerder hebt gedaan, is het een goed idee om in je debugger te stappen en precies te zien hoe het woordenboek elke functie opzoekt.

OPMERKING: Gebruik geen“()” in de hoofdlettergebruik/woordenboek-lookup, anders roept het elk van uw functies aan als het woordenboek/hoofdletterblok wordt gemaakt. Onthoud dit omdat je elke functie maar één keer wilt aanroepen met behulp van een hash-stijl lookup.

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

Antwoord 15

De meeste antwoorden hier zijn behoorlijk oud, en vooral de geaccepteerde, dus het lijkt de moeite waard om te updaten.

Eerst de officiële Python FAQbehandelt dit, en beveelt de keten elifaan voor eenvoudige gevallen en het dictvoor grotere of complexere gevallen. Het suggereert in sommige gevallen ook een reeks visit_-methoden (een stijl die door veel serverframeworks wordt gebruikt):

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

De FAQ vermeldt ook PEP 275, dat is geschreven om een officiële eens-en-voor-al beslissing over het toevoegen van C-stijl switch statements. Maar die PEP werd eigenlijk uitgesteld tot Python 3, en het werd pas officieel afgewezen als een afzonderlijk voorstel, PEP 3103. Het antwoord was natuurlijk nee, maar de twee PEP’s hebben links naar aanvullende informatie als je geïnteresseerd bent in de redenen of de geschiedenis.


Eén ding dat meerdere keren naar voren kwam (en te zien is in PEP 275, hoewel het werd weggelaten als een echte aanbeveling), is dat als je echt last hebt van 8 regels code om 4 gevallen af te handelen, versus . de 6 regels die je zou hebben in C of Bash, je kunt altijd dit schrijven:

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

Dit wordt niet bepaald aangemoedigd door PEP 8, maar het is leesbaar en niet al te eenduidig.


In de meer dan tien jaar sinds PEP 3103 werd afgewezen, is de kwestie van C-stijl case-statements, of zelfs de iets krachtigere versie in Go, als dood beschouwd; wanneer iemand het op python-ideeën of -dev brengt, worden ze verwezen naar de oude beslissing.

Het idee van volledige patroonovereenkomst in ML-stijl ontstaat echter om de paar jaar, vooral sinds talen als Swift en Rust het hebben overgenomen. Het probleem is dat het moeilijk is om veel gebruik te maken van patroonovereenkomst zonder algebraïsche gegevenstypen. Hoewel Guido sympathie heeft voor het idee, komt niemand met een voorstel dat heel goed in Python past. (Je kunt mijn stroman van 2014lezen bijvoorbeeld.) Dit zou kunnen veranderen met dataclassin 3.7 en enkele sporadische voorstellen voor een krachtigere enumom somtypen te verwerken, of met verschillende voorstellen voor verschillende soorten verklaringen- lokale bindingen (zoals PEP 3150, of de reeks voorstellen die momenteel worden besproken op -ideeën). Maar tot nu toe niet.

Er zijn ook af en toe voorstellen voor Perl 6-stijl matching, wat in feite een mengelmoes is van alles, van eliftot regex tot single-dispatch type-switching.


Antwoord 16

Uitbreiding van het ‘dict as switch’-idee. Als u een standaardwaarde voor uw schakelaar wilt gebruiken:

def f(x):
    try:
        return {
            'a': 1,
            'b': 2,
        }[x]
    except KeyError:
        return 'default'

Antwoord 17

Ik ontdekte dat een veelvoorkomende schakelstructuur:

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

kan als volgt in Python worden uitgedrukt:

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

of op een duidelijkere manier opgemaakt:

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

In plaats van een statement te zijn, is de Python-versie een expressie die evalueert naar een waarde.


Antwoord 18

De oplossingen die ik gebruik:

Een combinatie van 2 van de hier geposte oplossingen, die relatief gemakkelijk te lezen is en standaardinstellingen ondersteunt.

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

waar

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

zoekt "lambda x: x - 2"op in het dictaat en gebruikt het met x=23

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

vindt het niet in het dictaat en gebruikt de standaard "lambda x: x - 22"met x=44.


Antwoord 19

Ik heb het eenvoudige antwoord dat ik zocht nergens gevonden op Google Zoeken. Maar ik kwam er in ieder geval achter. Het is echt heel simpel. Besloten om het te posten, en misschien een paar krasjes minder op het hoofd van iemand anders te voorkomen. De sleutel is gewoon “in” en tupels. Hier is het gedrag van de switch-statement met fall-through, inclusief RANDOM 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)

Biedt:

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.

Antwoord 20

# 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

Antwoord 21

def f(x):
    dictionary = {'a':1, 'b':2, 'c':3}
    return dictionary.get(x,'Not Found') 
##Returns the value for the letter x;returns 'Not Found' if x isn't a key in the dictionary

Antwoord 22

U kunt een verzonden dictaat gebruiken:

#!/usr/bin/env python
def case1():
    print("This is case 1")
def case2():
    print("This is case 2")
def case3():
    print("This is case 3")
token_dict = {
    "case1" : case1,
    "case2" : case2,
    "case3" : case3,
}
def main():
    cases = ("case1", "case3", "case2", "case1")
    for case in cases:
        token_dict[case]()
if __name__ == '__main__':
    main()

Uitvoer:

This is case 1
This is case 3
This is case 2
This is case 1

Antwoord 23

Ik vond het antwoord van Mark Bies’s antwoord

leuk

Omdat de variabele xtwee keer moet worden gebruikt, heb ik de lambda-functies gewijzigd in parameterloos.

Ik moet werken met 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'

Bewerken:ik heb gemerkt dat ik het type Nonekan gebruiken met woordenboeken. Dit zou dus switch ; case else


Antwoord 24

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

Kort en gemakkelijk te lezen, heeft een standaardwaarde en ondersteunt uitdrukkingen in zowel voorwaarden als retourwaarden.

Het is echter minder efficiënt dan de oplossing met een woordenboek. Python moet bijvoorbeeld alle voorwaarden doorzoeken voordat de standaardwaarde wordt geretourneerd.


Antwoord 25

Eenvoudig, niet getest; elke voorwaarde wordt onafhankelijk geëvalueerd: er is geen fall-through, maar alle gevallen worden geëvalueerd (hoewel de expressie die moet worden ingeschakeld slechts één keer wordt geëvalueerd), tenzij er een break-statement is. Bijvoorbeeld,

for case in [expression]:
    if case == 1:
        print(end='Was 1. ')
    if case == 2:
        print(end='Was 2. ')
        break
    if case in (1, 2):
        print(end='Was 1 or 2. ')
    print(end='Was something. ')

prints Was 1. Was 1 or 2. Was something.(Verdorie! Waarom mag ik geen witruimte achteraan in inline codeblokken?)if expressionresulteert in 1, Was 2.als expressionresulteert in 2, of Was something.als expressionevalueert naar iets anders.


Antwoord 26

Er zijn tot nu toe veel antwoorden geweest die zeiden: “we hebben geen schakelaar in Python, doe het op deze manier”. Ik wil er echter op wijzen dat de switch-instructie zelf een gemakkelijk te misbruiken constructie is die in de meeste gevallen kan en moet worden vermeden omdat ze luie programmering bevorderen. Voorbeeld:

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

Nu, je zoudit kunnen doen met een switch-statement (als Python er een zou aanbieden), maar je zou je tijd verspillen omdat er methoden zijn die dit prima doen. Of misschien heb je iets minder voor de hand liggends:

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

Dit soort bewerkingen kunnen en moeten echter worden afgehandeld met een woordenboek, omdat het sneller, minder complex, minder foutgevoelig en compacter is.

En de overgrote meerderheid van “use cases” voor switch-statements zal in een van deze twee gevallen vallen; er is gewoon heel weinig reden om er een te gebruiken als je goed over je probleem hebt nagedacht.

Dus, in plaats van te vragen “hoe schakel ik over in Python?”, moeten we misschien vragen: “Waarom wil ik overschakelen in Python?” want dat is vaak de interessantere vraag en zal vaak gebreken aan het licht brengen in het ontwerp van wat je ook aan het bouwen bent.

Dat wil niet zeggen dat schakelaars ook nooit mogen worden gebruikt. Staatsmachines, lexers, parsers en automaten gebruiken ze allemaal tot op zekere hoogte en, in het algemeen, als je begint met een symmetrische invoer en naar een asymmetrische uitvoer gaat, kunnen ze nuttig zijn; je moet er alleen voor zorgen dat je de schakelaar niet als een hamer gebruikt, want je ziet een hoop spijkers in je code.


Antwoord 27

Een oplossing die ik meestal gebruik die ook gebruik maakt van woordenboeken is:

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

Dit heeft het voordeel dat er niet elke keer wordt geprobeerd de functies te evalueren, en je moet er alleen voor zorgen dat de uiterlijke functie alle informatie krijgt die de innerlijke functies nodig hebben.


Antwoord 28

Ik was nogal in de war na het lezen van het geaccepteerde antwoord, maar dit maakte alles duidelijk:

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

Deze code is analoog aan:

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

Bekijk de Bronvoor meer over woordenboek toewijzen aan functies.


Antwoord 29

Definiëren:

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

hiermee kunt u een vrij eenvoudige syntaxis gebruiken, waarbij de gevallen op een kaart zijn gebundeld:

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

Ik bleef proberen om schakelaar zo te herdefiniëren dat ik de “lambda:” kwijt zou raken, maar gaf het op. De definitie aanpassen:

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

Laat me toe om meerdere gevallen toe te wijzen aan dezelfde code, en om een standaardoptie op te geven:

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")
    })

Elk gerepliceerd geval moet in zijn eigen woordenboek staan; switch() consolideert de woordenboeken voordat de waarde wordt opgezocht. Het is nog steeds lelijker dan ik zou willen, maar het heeft de basisefficiëntie van het gebruik van een gehashte lookup op de uitdrukking, in plaats van een lus door alle sleutels.


Antwoord 30

Uitbreiden op Het antwoord van Greg Hewgill– We kunnen de woordenboekoplossing inkapselen met behulp van een decorateur:

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

Dit kan dan worden gebruikt met de @case-decorator

@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

Het goede nieuws is dat dit al is gedaan in NeoPySwitch– module. Installeer eenvoudig met pip:

pip install NeoPySwitch

Other episodes