Замены для оператора switch в Python?

Я хочу написать функцию в Python, которая возвращает разные фиксированные значения на основе значения входного индекса.

В других языках я бы использовал оператор switch или case , но у Python не было оператора switch . Каковы рекомендуемые решения Python в этом сценарии?

  • Словарь Python vs Если скорость сообщения
  • Словарь или инструкции if, Jython
  • Может ли оператор switch иметь более одной переменной?
  • ключи переключения и значения в словаре в python
  • Выбор между различными заменами ключей в Python - словаре или if-elif-else?
  • Что такое эквивалент Python для оператора case / switch?
  • У Python есть эквивалент «switch»?
  • 30 Solutions collect form web for “Замены для оператора switch в Python?”

    Вы можете использовать словарь:

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

    Если вы хотите использовать по умолчанию, вы можете использовать метод get(key[, default]) :

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

    Мне всегда нравилось делать это так

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

    Отсюда

    В дополнение к методам словаря (которые мне очень нравятся, BTW) вы также можете использовать if-elif-else для получения функций 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 

    Это, конечно, не идентично переключателю / случаю – вы не можете проваливаться так же легко, как оставлять перерыв; , но вы можете провести более сложный тест. Его форматирование лучше, чем серия вложенных ifs, хотя функционально это то, чем ближе.

    Мой любимый рецепт Python для переключателя / случая:

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

    Короткие и простые для простых сценариев.

    Сравните с 11 + строками кода C:

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

    Вы можете даже назначить несколько переменных с помощью кортежей:

     choices = {'a': (1, 2, 3), 'b': (4, 5, 6)} (result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3')) 
     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)) 

    Применение:

     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 

    тесты:

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

    Есть образец, который я узнал из кода 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' 

    Вы можете использовать его в любое время, когда вам нужно отправить маркер и выполнить расширенный фрагмент кода. В государственной машине у вас будут методы state_ и отправка на self.state . Этот коммутатор может быть просто расширен, наследуя базовый класс и определяя ваши собственные методы do_ . Часто у вас даже не будет do_ методов в базовом классе.

    Изменить: как именно это используется

    В случае SMTP вы получите HELO с провода. Соответствующий код (из twisted/mail/smtp.py , измененный для нашего случая) выглядит следующим образом:

     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 

    Вы получите ' HELO foo.bar.com ' (или вы можете получить 'QUIT' или 'RCPT TO: foo' ). Это обозначается как ['HELO', 'foo.bar.com'] . Фактическое имя поиска метода взято из parts[0] .

    (Исходный метод также называется state_COMMAND , потому что он использует тот же шаблон для реализации getattr(self, 'state_' + self.mode) автомата, то есть getattr(self, 'state_' + self.mode) )

    Мой любимый – очень хороший рецепт . Тебе это действительно понравится. Это самый близкий из тех, что я видел в реальных операциях оператора case, особенно в функциях.

    Вот пример:

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

    Предположим, вы не хотите просто возвращать значение, но хотите использовать методы, которые что-то меняют на объекте. Используя описанный здесь подход, можно было бы:

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

    Что здесь происходит, так это то, что python оценивает все методы в словаре. Поэтому, даже если ваше значение равно «a», объект будет увеличиваться и уменьшаться на x.

    Решение:

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

    Таким образом, вы получаете список, содержащий функцию и ее аргументы. Таким образом, возвращается только указатель функции и список аргументов, а не вычисляются. 'result' затем оценивает вызов возвращенной функции.

     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, cond, *mconds): return self._val in (cond,)+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 

    расширяя идею «dict as switch». если вы хотите использовать значение по умолчанию для вашего коммутатора:

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

    Если вы ищете дополнительный оператор, как «switch», я построил модуль python, который расширяет Python. Он называется ESPY как «Расширенная структура для Python», и он доступен как для Python 2.x, так и для Python 3.x.

    Например, в этом случае оператор switch может быть выполнен следующим кодом:

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

    который можно использовать следующим образом:

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

    поэтому espy переводит его в Python как:

     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 

    Если у вас есть сложный блок, вы можете рассмотреть таблицу поиска словаря функций …

    Если вы еще не сделали этого, прежде чем приступить к входу в ваш отладчик и посмотреть, как именно словарь ищет каждую функцию.

    ПРИМЕЧАНИЕ. Не используйте «()» внутри поиска / словаря или он будет вызывать каждую из ваших функций при создании блока словаря / случая. Помните об этом, потому что вы хотите только один раз вызвать каждую функцию, используя поиск стиля хэш.

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

    Я не нашел простой ответ, который я искал в любом месте в поиске Google. Но я все равно понял это. Это очень просто. Решил опубликовать его и, возможно, предотвратить еще несколько царапин на чужой голове. Ключ – это просто «в» и кортежи. Вот поведение оператора switch с провалом, включая провал RANDOM.

     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) 

    Обеспечивает:

     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. 

    Я обнаружил, что общая коммутационная структура:

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

    может быть выражена в Python следующим образом:

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

    или форматировать более четко:

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

    Вместо того, чтобы быть инструкцией, версия python представляет собой выражение, которое оценивает значение.

    Решения, которые я использую:

    Комбинация двух из решений, размещенных здесь, что относительно легко читать и поддерживает значения по умолчанию.

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

    где

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

    ищет "lambda x: x - 2" в dict и использует его с x=23

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

    не находит его в dict и использует по умолчанию "lambda x: x - 22" с x=44 .

     # 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 

    Мне понравился ответ Марка Биса

    Поскольку переменная x должна использоваться дважды, я изменил функции лямбда на без параметров.

    Мне нужно запустить с 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' 

    Edit: Я заметил, что я могу использовать тип None со словарями. Таким образом, это будет эмулировать switch ; case else switch ; case else

    Я сделал (относительно) гибкое и повторно используемое решение для этого. Его можно найти в GitHub как этот смысл . Если результат функции коммутатора является вызываемым, он автоматически вызывается.

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

    Короткое и легко читаемое значение имеет значение по умолчанию и поддерживает выражения как в условиях, так и в возвращаемых значениях.

    Однако он менее эффективен, чем решение со словарем. Например, Python должен проверять все условия перед возвратом значения по умолчанию.

    Определение:

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

    позволяет использовать довольно простой синтаксис, в случае случаев, связанных с картой:

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

    Я продолжал пытаться переопределить переключатель таким образом, чтобы я мог избавиться от «лямбда», но сдался. Тщательное определение:

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

    Позвольте мне сопоставить несколько случаев с одним и тем же кодом и предоставить опцию по умолчанию:

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

    Каждый реплицированный случай должен быть в его собственном словаре; switch () объединяет словари, прежде чем искать значение. Он все еще уродливее, чем хотелось бы, но он имеет основную эффективность использования хешированного поиска в выражении, а не цикла через все ключи.

    Я думаю, что лучший способ – использовать идиомы языка python, чтобы ваш код мог тестироваться . Как показано в предыдущих ответах, я использую словари, чтобы использовать структуры и язык python и изолировать код «case» в разных методах. Ниже приведен класс, но вы можете напрямую использовать модуль, глобалы и функции. Класс имеет методы, которые можно протестировать с помощью изоляции . В зависимости от ваших потребностей вы также можете играть со статическими методами и атрибутами.

     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) 

    Этот метод можно использовать, используя также классы как ключи «__choice_table». Таким образом, вы можете избежать злоупотреблений и сохранить все чистое и проверяемое.

    Предположим, вам нужно обработать много сообщений или пакетов из сети или вашего MQ. Каждый пакет имеет свою структуру и код управления (в общем виде). С приведенным выше кодом можно сделать что-то вроде этого:

     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) 

    Таким образом, сложность не распространяется в потоке кода, но отображается в структуре кода .

    Если вы не беспокоитесь о потере подсветки синтаксиса внутри корпусов, вы можете сделать следующее:

     exec { 1: """ print ('one') """, 2: """ print ('two') """, 3: """ print ('three') """, }.get(value, """ print ('None') """) 

    Где value – это значение. В C это будет:

     switch (value) { case 1: printf("one"); break; case 2: printf("two"); break; case 3: printf("three"); break; default: printf("None"); break; } 

    Мы также можем создать вспомогательную функцию для этого:

     def switch(value, cases, default): exec cases.get(value, default) 

    Поэтому мы можем использовать его так, например, с одним, двумя и тремя:

     switch(value, { 1: """ print ('one') """, 2: """ print ('two') """, 3: """ print ('three') """, }, """ print ('None') """) 

    Я бы просто использовал if / elif / else. Я думаю, что это достаточно хорошо, чтобы заменить оператор switch.

    Расширение ответа Грега Хьюджилла. Мы можем инкапсулировать словарь-решение с помощью декоратора:

     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 

    This can then be used with the @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 

    The good news are that this has already been done in NeoPySwitch -module. Simply install using pip:

     pip install NeoPySwitch 

    I'm just going to drop my two cents in here. The reason there isn't a case/switch statement in Python is because Python follows the principle of 'Theres only one right way to do something'. So obviously you could come up with various ways of recreating switch/case functionality, but the Pythonic way of accomplishing this is the if/elif construct. т.е.

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

    I just felt PEP 8 deserved a nod here. One of the beautiful things about Python is its simplicity and elegance. That is largely derived from principles laid our in PEP 8, including "There's only one right way to do something"

    For the sake of completeness, here are some of my attempts back in stone-age:

    http://code.activestate.com/recipes/269708-some-python-style-switches/?in=user-1521341

    I especially enjoy the use of "3. Select values with 'range comparisons'"

    If you are really just returning a predetermined, fixed value, you could create a dictionary with all possible input indexes as the keys, along with their corresponding values. Also, you might not really want a function to do this – unless you're computing the return value somehow.

    Oh, and if you feel like doing something switch-like, see here .

    I was quite confused after reading the answer, but this cleared it all up:

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

    This code is analogous to:

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

    Check the Source for more about dictionary mapping to functions.

    Greg's solutions will not work for unhashable entries. For example when indexing lists .

     # doesn't work def give_me_array(key) return { [1, 0]: "hello" }[key] 

    Luckily though tuples are hashable.

     # works def give_me_array(key) return { (1, 0): "hello" }[tuple(key)] 

    Similarly there probably are immutable (thus probably hashable) versions of dictionaries or sets too.

    Python - лучший язык программирования в мире.