Pregunta ¿Reemplazos para la declaración del interruptor en Python?


Quiero escribir una función en Python que devuelva diferentes valores fijos basados ​​en el valor de un índice de entrada.

En otros idiomas usaría un switch o case declaración, pero Python no parece tener una switch declaración. ¿Cuáles son las soluciones de Python recomendadas en este escenario?


1440
2017-09-13 00:36


origen


Respuestas:


Puedes usar un diccionario:

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

1125
2017-09-13 00:38



Si desea valores predeterminados, puede usar el diccionario get(key[, default]) método:

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

1108
2017-09-19 15:45



Siempre me ha gustado hacerlo de esta manera

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

De aquí


293
2017-09-13 00:41



Además de los métodos del diccionario (que realmente me gustan, BTW), también puede usar if-elif-else para obtener la funcionalidad 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

Esto, por supuesto, no es idéntico al cambio / caso: no se puede pasar tan fácilmente como dejar el receso; declaración, pero puede tener una prueba más complicada. Su formateo es más agradable que una serie de if anidados, aunque funcionalmente eso es lo que más se acerca.


248
2017-09-13 01:10



Mi receta favorita de Python para el interruptor / caja es:

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

Corto y simple para escenarios simples.

Compare con más de 11 líneas de código C:

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

Incluso puede asignar múltiples variables mediante el uso de tuplas:

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

Uso:

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

Pruebas:

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



Hay un patrón que aprendí del código de Twisted Python.

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'

Puedes utilizarlo en cualquier momento que necesites enviar un token y ejecutar un fragmento de código ampliado. En una máquina de estado, tendrías state_ métodos y despacho en self.state. Este interruptor puede extenderse limpiamente al heredar de la clase base y definir su propio do_ métodos. Muchas veces ni siquiera tendrás do_ métodos en la clase base.

Editar: cómo exactamente es eso usado

En el caso de SMTP, recibirá HELO del cable. El código relevante (de twisted/mail/smtp.py, modificado para nuestro caso) se ve así

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

Recibirás ' HELO foo.bar.com ' (o puede obtener 'QUIT' o 'RCPT TO: foo') Esto está tokenizado en parts como ['HELO', 'foo.bar.com']. El nombre de búsqueda del método real está tomado de parts[0].

(El método original también se llama state_COMMAND, porque usa el mismo patrón para implementar una máquina de estado, es decir getattr(self, 'state_' + self.mode))


41
2017-09-13 01:26



Mi favorito es realmente agradable receta. Realmente te gustará. Es el más cercano que he visto a las declaraciones de cambio reales, especialmente en las características.

Aquí hay un ejemplo:

# 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