Vraag Vervangingen voor schakelinstructie in Python?


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

In andere talen zou ik een switch of case verklaring, maar Python lijkt er geen te hebben switch uitspraak. Wat zijn de aanbevolen Python-oplossingen in dit scenario?


1440
2017-09-13 00:36


oorsprong


antwoorden:


U zou een woordenboek kunnen gebruiken:

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

1125
2017-09-13 00:38



Als u standaardinstellingen wilt, kunt u het woordenboek gebruiken get(key[, default]) methode:

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

1108
2017-09-19 15:45



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


293
2017-09-13 00:41



Naast de woordenboekmethoden (die ik echt leuk vind, BTW), kun je ook if-elif-else gebruiken om de switch / case / standaardfunctionaliteit te verkrijgen:

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 hetzelfde als schakelen / omkeren - je kunt niet zo gemakkelijk doorvallen als het verlaten van de pauze; verklaring, maar u kunt een ingewikkelder test doen. De opmaak is leuker dan een reeks geneste if's, hoewel functioneel, daar is het dichter bij.


248
2017-09-13 01:10



Mijn favoriete Python-recept voor schakelaar / case is:

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

Kort en eenvoudig voor eenvoudige scenario's.

Vergelijk met 11 + regels met 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;
}

U kunt zelfs meerdere variabelen toewijzen met behulp van tuples:

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

122
2018-06-17 02:25



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

Tests:

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

85
2017-07-07 06:09



Er is een patroon dat ik heb geleerd van de 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 elke keer dat je het op een token moet verzenden en een uitgebreid stuk code moet uitvoeren. In een staatsmachine die je zou hebben state_ methoden en verzending aan self.state. Deze switch kan netjes worden uitgebreid door de basisklasse te overerven en je eigen te definiëren do_ methoden. Vaak heb je het niet eens do_ methoden in de basisklasse.

Bewerken: hoe wordt dat precies gebruikt

In het geval van SMTP ontvangt u HELO van de draad. De relevante code (van twisted/mail/smtp.py, aangepast voor onze zaak) 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 zult ontvangen ' HELO foo.bar.com ' (of je zou kunnen krijgen 'QUIT' of 'RCPT TO: foo'). Dit is getokeniseerd in parts als ['HELO', 'foo.bar.com']. De naam van de naam van de daadwerkelijke methode is overgenomen van parts[0].

(De oorspronkelijke methode wordt ook wel genoemd state_COMMANDomdat het hetzelfde patroon gebruikt om een ​​toestandsmachine te implementeren, d.w.z. getattr(self, 'state_' + self.mode))


41
2017-09-13 01:26



Mijn favoriete is echt heel leuk recept. Je zult het echt leuk vinden. Het is het dichtst dat ik heb gezien om case-statements uit te wisselen, vooral in functies.

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

38
2017-07-07 06:12



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

28
2018-05-03 09:05