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

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

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

13.09.2008 00:36:30
Связанный PEP, созданный самим Гвидо: PEP 3103
chb 16.06.2012 17:22:39
@chb В этом PEP Гвидо не упоминает, что цепочки if / elif также являются классическим источником ошибок. Это очень хрупкая конструкция.
itsbruce 9.01.2014 13:46:25
Во всех решениях здесь отсутствует обнаружение повторяющихся значений регистра . Как принцип отказоустойчивости, это может быть более важной потерей, чем производительность или прорывная функция.
Bob Stein 17.10.2014 19:04:50
switchна самом деле является более «универсальным», чем то, что возвращает разные фиксированные значения, основанные на значении входного индекса. Это позволяет выполнять разные части кода. На самом деле даже не нужно возвращать значение. Интересно, являются ли некоторые ответы здесь хорошей заменой для общего switchутверждения или только для случая возврата значений без возможности выполнения общих частей кода.
sancho.s ReinstateMonicaCellio 14.05.2017 21:29:47
@ MalikA.Rumi Хрупкая конструкция, так же как цикл while является хрупкой конструкцией, если вы пытаетесь использовать ее, чтобы делать то, что для ... в ... делает. Вы собираетесь назвать программистов слабыми для использования для циклов? Пока петли - это все, что им действительно нужно. Но петли показывают четкое намерение, сохраняют бессмысленный шаблон и дают возможность создавать мощные абстракции.
itsbruce 1.10.2017 06:53:16
30 ОТВЕТОВ
РЕШЕНИЕ

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

def f(x):
    return {
        'a': 1,
        'b': 2,
    }[x]
1474
13.09.2008 00:38:24
Что произойдет, если х не найден?
Nick 19.09.2008 15:46:18
@nick: вы можете использовать defaultdict
Eli Bendersky 19.09.2008 17:38:58
Я бы порекомендовал ставить dict вне функции, если производительность является проблемой, поэтому он не перестраивает dict при каждом вызове функции
Claudiu 23.10.2008 16:22:08
@EliBendersky, Использование getметода, вероятно, будет более нормальным, чем использование collections.defaultdictв этом случае.
Mike Graham 23.02.2012 16:38:06
@Nick, выдается исключение - делайте }.get(x, default)вместо этого, если должно быть значение по умолчанию. (Примечание: это намного лучше, чем то, что происходит, если вы оставите значение по умолчанию выключенным оператором switch!)
Mike Graham 23.02.2012 16:39:14

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

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

Отсюда

388
16.02.2014 05:06:42
Отличный метод в сочетании с get () для обработки
drAlberT 2.09.2009 16:11:41
возможно, в этом случае не очень хорошая идея использовать лямбду, потому что лямбда на самом деле вызывается каждый раз, когда создается словарь.
Asher 22.04.2012 21:48:36
К сожалению, это самые близкие люди собираются получить. Методы, которые используют .get()(как текущие самые высокие ответы), должны будут с нетерпением оценить все возможности перед отправкой, и поэтому не только (не только очень, но) чрезвычайно неэффективны и также не могут иметь побочных эффектов; этот ответ обходит эту проблему, но более многословен. Я бы просто использовал if / elif / else, и даже те, которые пишутся так же, как и case, занимают столько же времени.
ninjagecko 17.03.2014 13:48:35
Разве это не будет оценивать все функции / лямбды каждый раз во всех случаях, даже если он возвращает только один из результатов?
slf 6.08.2014 19:04:35
@slf Нет, когда поток управления достигает этого куска кода, он создаст 3 функции (используя 3 лямбда-выражения), а затем создаст словарь с этими тремя функциями в качестве значений, но они останутся невостребованными ( оценка немного неоднозначна в этот контекст) на первых порах. Затем словарь индексируется через [value], который будет возвращать только одну из 3 функций (при условии, что valueэто один из 3 ключей). Эта функция еще не была вызвана. Затем (x)вызывает только что возвращенную функцию с xаргументом as (и результат отправляется в result). Другие 2 функции не будут вызваны.
blubberdiblub 18.09.2015 06:11:11

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

О, и если вы чувствуете, что делаете что-то вроде переключателя, смотрите здесь .

0
20.01.2016 16:21:52

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

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

345
19.11.2019 19:53:47
я бы действительно предпочел это, он использует стандартную языковую конструкцию и не выдает KeyError, если не найдено ни одного соответствующего случая
martyglaubitz 18.05.2013 10:30:13
Я думал о словаре / getспособе, но стандартный способ просто более читабелен.
Martin Thoma 25.06.2015 06:33:35
@ Someuser, но тот факт, что они могут «перекрывать», является особенностью. Вы просто убедитесь, что порядок является приоритетом, в котором должны происходить совпадения. Что касается повторного х: просто сделайте x = the.other.thingдо. Как правило, у вас будет один if, несколько elif и еще один, поскольку это легче понять.
Matthew Schinckel 3.03.2016 06:55:38
Хорошо, "провал, не используя elif", немного сбивает с толку. Как насчет этого: забыть о «провалиться» и просто принять это как два if/elif/else?
Alois Mahdal 30.05.2016 11:40:41
Также стоит отметить, что при использовании таких вещей, как x in 'bc', имейте в виду, что "" in "bc"это так True.
Lohmar ASHAR 28.08.2018 11:46:21

Есть шаблон, который я узнал из кода 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'). Это обозначено partsкак ['HELO', 'foo.bar.com']. Фактическое имя поиска метода взято из parts[0].

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

47
13.09.2008 17:42:03
Я не вижу выгоды от этого паттерна по сравнению с прямым вызовом методов: SMTP (). Do_HELO ('foo.bar.com') Хорошо, в lookupMethod может быть общий код, но так как он также может быть перезаписан Подкласс Я не вижу, что вы получаете от косвенности.
Mr Shark 13.09.2008 11:35:11
Вы не знаете заранее, какой метод вызывать, то есть HELO происходит из переменной. я добавил пример использования в исходное сообщение
user6205 13.09.2008 17:45:31
Могу я предложить просто: eval ('SMTP (). Do_' + команда) ('foo.bar.com')
jforberg 21.06.2011 17:32:16
Eval? шутки в сторону? и вместо того, чтобы создавать один метод для каждого вызова, мы можем очень легко создать один экземпляр и использовать его во всех вызовах, если у него нет внутреннего состояния.
Mahesh 19.03.2013 18:10:52
IMO, настоящий ключ здесь - диспетчеризация с использованием getattr для указания функции для запуска. Если бы методы были в модуле, вы могли бы получить getattr (locals (), func_name), чтобы получить его. Часть 'do_' хороша для безопасности / ошибок, поэтому могут вызываться только функции с префиксом. SMTP сам вызывает lookupMethod. В идеале снаружи ничего об этом не знает. Это не имеет смысла делать SMTP (). LookupMethod (имя) (данные). Поскольку команда и данные находятся в одной строке и SMTP анализирует ее, это имеет больше смысла. Наконец, SMTP, вероятно, имеет другое общее состояние, которое оправдывает его как класс.
ShawnFumo 15.08.2013 22:17:10

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

def f(x):
    try:
        return {
            'a': 1,
            'b': 2,
        }[x]
    except KeyError:
        return 'default'
17
19.09.2008 15:37:15
Я думаю, что яснее использовать .get () в dict с указанным по умолчанию. Я предпочитаю оставлять исключения для исключительных обстоятельств, и это сокращает три строки кода и уровень отступа, не будучи неясным.
Chris B. 5.06.2009 15:14:41
Это является исключительным обстоятельством. Это может быть или не быть редким обстоятельством, зависящим от полезного, но это определенно исключение (отступление 'default') из правила (получить что-то из этого диктата). По замыслу, программы на Python используют исключения без промедления. При этом использование getможет потенциально сделать код немного приятнее.
Mike Graham 26.03.2010 16:49:13

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

def f(x):
    return {
        'a': 1,
        'b': 2
    }.get(x, 9)    # 9 is default if x not found
1363
6.07.2017 22:51:30
Что если «a» и «b» соответствуют 1, а «c» и «d» соответствуют 2?
John Mee 9.04.2010 07:57:40
@JM: Ну, очевидно, поиск по словарю не поддерживает провалы. Вы можете сделать двойной поиск по словарю. Т.е. «a» и «b» указывают на answer1, а «c» и «d» указывают на answer2, которые содержатся во втором словаре.
Nick 9.04.2010 09:54:55
лучше передать значение по умолчанию
HaTiMSuM 13.10.2016 11:01:54
С этим подходом есть проблемы: во-первых, каждый раз, когда вы вызываете f, вы снова создаете dict, во-вторых, если у вас более сложное значение, вы можете получить исключения ex. если x - кортеж, и мы хотим сделать что-то вроде этого x = ('a') def f (x): return {'a': x [0], 'b': x [1]} .get ( x [0], 9) Это вызовет ошибку IndexError
Idan Haim Shalom 30.08.2017 08:01:47
@Idan: Вопрос в том, чтобы повторить переключатель. Я уверен, что я мог бы также сломать этот код, если бы попытался ввести нечетные значения. Да, он будет воссоздан, но это легко исправить.
Nick 19.09.2017 21:59:49

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

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

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"в диктанте и использует его сx=23

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

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

12
23.02.2012 16:27:48

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

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)

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

26
30.09.2010 08:31:55

Если вы ищете дополнительный оператор, например «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
16
28.08.2018 15:09:16
Очень круто, но какой смысл while True:в верхней части сгенерированного кода Python? Он неизбежно попадет breakв конец сгенерированного кода Python, поэтому мне кажется, что while True:и breakможно удалить. Кроме того, достаточно ли ESPY умен, чтобы изменить имя, contесли пользователь использует это же имя в своем собственном коде? В любом случае, я хочу использовать ванильный Python, поэтому я не буду его использовать, но, тем не менее, это круто. +1 за чистую крутость.
ArtOfWarfare 29.06.2014 12:56:50
@ArtOfWarfare Причина while True:и breaks заключается в том, чтобы разрешить, но не требовать провала .
Solomon Ucko 23.03.2019 14:10:44
Этот модуль все еще доступен?
Solomon Ucko 24.03.2019 00:57:56
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.
102
31.05.2013 14:24:52
Это небезопасно. Если несколько переключателей нажаты одновременно, все переключатели принимают значение последнего переключателя.
francescortiz 26.06.2013 16:35:23
Хотя @francescortiz скорее всего означает потокобезопасность, это также не безопасно. Это угрожает значениям переменных!
Zizouz212 16.06.2015 16:29:01
Вероятно, проблему безопасности потока можно обойти, используя локальное хранилище потока . Или этого можно избежать, просто возвращая экземпляр и используя его для сравнения случаев.
blubberdiblub 18.09.2015 06:24:50
@blubberdiblub Но разве не эффективнее использовать стандартное ifутверждение?
wizzwizz4 23.05.2016 17:32:06
Это также не безопасно, если используется в нескольких функциях. В приведенном примере, если case(2)блок вызвал другую функцию, которая использует switch (), то при выполнении case(2, 3, 5, 7)etc для поиска следующего выполняемого случая он будет использовать значение switch, установленное другой функцией, а не значение, установленное текущим оператором switch. ,
user9876 17.08.2017 08:58:22

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

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

Вот пример:

# 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!"
50
24.03.2019 02:36:28
Я хотел бы заменить for case in switch()с with switch() as case, имеет больше смысла, так как это нужно с , чтобы запустить только один раз.
Ski 12.12.2013 16:24:51
@Skirmantas: Обратите внимание, что withэто не breakучитывает, поэтому опцию отмены.
Jonas Schäfer 8.05.2014 16:53:10
Извиняюсь за то, что не приложил больше усилий, чтобы определить это сам: подобный ответ выше не является потокобезопасным. Это?
David Winiecki 12.09.2014 15:47:16
@DavidWiniecki Компоненты кода, отсутствующие в указанном выше (и, возможно, авторские права на activestate), по-видимому, являются потокобезопасными.
Jasen 23.08.2016 22:09:30
Будет ли другая версия этого что-то вроде if c in set(range(0,9)): print "digit" elif c in set(map(chr, range(ord('a'), ord('z')))): print "lowercase"?
mpag 31.10.2016 19:08:18

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

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

Примечание: не используйте «()» в случае / поиск в словаре , или он будет вызывать каждый из ваших функций , как создается словарь / корпус блока. Запомните это, потому что вы хотите вызывать каждую функцию только один раз, используя поиск в хэш-стиле.

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()
16
17.10.2012 21:52:45
Мне нравится ваше решение. Но что, если мне просто нужно передать некоторые переменные или объекты?
Tedo Vrbanec 19.12.2018 20:27:27
Это не будет работать, если метод ожидает параметры.
Kulasangar 29.01.2020 06:35:54
def f(x):
     return 1 if x == 'a' else\
            2 if x in 'bcd' else\
            0 #default

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

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

7
5.11.2012 20:05:12

Простое сопоставление некоторого ключа с некоторым кодом на самом деле не так важно, как показало большинство людей с помощью dict. Настоящий трюк заключается в том, чтобы попытаться эмулировать весь процесс и сломать его. Я не думаю, что когда-либо писал заявление о ситуации, где я использовал эту «особенность». Вот, что нужно сделать.

def case(list): reduce(lambda b, f: (b | f[0], {False:(lambda:None),True:f[1]}[b | f[0]]())[0], list, False)

case([
    (False, lambda:print(5)),
    (True, lambda:print(4))
])

Я действительно представлял это как одно утверждение. Надеюсь, вы простите за глупое форматирование.

reduce(
    initializer=False,
    function=(lambda b, f:
        ( b | f[0]
        , { False: (lambda:None)
          , True : f[1]
          }[b | f[0]]()
        )[0]
    ),
    iterable=[
        (False, lambda:print(5)),
        (True, lambda:print(4))
    ]
)

Я надеюсь, что это правильный питон. Это должно дать вам падение. конечно, логические проверки могут быть выражениями, и если вы хотите, чтобы они оценивались лениво, вы можете обернуть их все в лямбду. Мне не составит труда заставить его принять после выполнения некоторых пунктов в списке. Просто сделайте кортеж (bool, bool, function), где второй bool указывает, будет ли пробиваться или падать.

3
4.02.2013 17:05:55
Drop-through неструктурирован. Вам это не нужно, это сделает ваш код сложнее в обслуживании, труднее избежать ошибок, и он забанен многими моими стандартами кодирования. Это похмелье от C. C не структурировался на 100%, оставил в drop-through, goto, continue, break. Большая часть его потомка скопировала его.
ctrl-alt-delor 11.02.2013 10:34:34

также используйте список для хранения дел и вызовите соответствующую функцию, выбрав

cases = ['zero()','one()','two()','three()']

def zero():
  print "method for 0 called..."
def one():
  print "method for 1 called..."
def two():
  print "method for 2 called..."
def three():
  print "method for 3 called..." 

i = int(raw_input("Enter choice between 0-3 "))

if(i<=len(cases)):
 exec(cases[i])
else:
 print "wrong choice"

также объяснил в винддеске

-1
6.06.2013 14:01:46
зачем использовать exec, когда вы можете хранить функциональные объекты в списке, а не в списке строк?
Jean-François Fabre♦ 25.05.2019 19:33:51

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

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 () объединяет словари перед поиском значения. Это все еще страшнее, чем хотелось бы, но в нем есть базовая эффективность использования хешированного поиска в выражении, а не цикла по всем ключам.

5
25.07.2013 18:23:33

Я не нашел ни одного простого ответа в поиске в 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.
13
12.10.2013 18:43:57
Где именно здесь это падение?
Jonas Schäfer 8.05.2014 16:56:10
К сожалению! Там есть провал, но я больше не участвую в переполнении стека. Не люблю их вообще. Мне нравится вклад других, но только не Stackoverflow. Если вы используете провал для FUNCTIONALITY, то вы хотите ЗАПИСАТЬ определенные условия во всех в одном операторе case в переключателе (catch all), пока не достигнете оператора break в переключателе.
JD Graham 30.07.2014 04:58:56
Здесь оба значения «Собака» и «Кошка» проходят через них и обрабатываются той же функциональностью, которая определяется как имеющая «четыре ноги». Это абстракция, эквивалентная абстракции, и различные значения обрабатываются тем же оператором case, где происходит разрыв.
JD Graham 30.07.2014 05:16:18
@JDGraham Я думаю, что Джонас имел в виду еще один аспект падения, который случается, когда программист иногда забывает написать breakв конце кода для a case. Но я думаю, что нам не нужен такой «прорыв» :)
Mikhail Batcer 12.08.2015 09:00:50

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

Поскольку 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'

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

8
23.05.2017 11:47:36
Разве дело None не подражает просто result[None]()?
Bob Stein 11.03.2015 15:19:57
Да, точно. Я имею в видуresult = {'a': 100, None:5000}; result[None]
guneysus 11.03.2015 15:52:54
Просто проверяя, что никто не думает, None:ведет себя как default:.
Bob Stein 11.03.2015 16:54:50

Я обнаружил, что общая структура переключателя:

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 является выражением, которое оценивается как значение.

15
29.05.2015 17:25:57
Кроме того, вместо параметра ... ... и р1 (х) , как о parameterиp1==parameter
Bob Stein 11.03.2015 15:28:04
@ BobStein-VisiBone привет, вот пример , который работает в моей питона сессии: f = lambda x: 'a' if x==0 else 'b' if x==1 else 'c'. Когда я позже позвонил f(2), я получил 'c'; f(1), 'b'; и f(0), 'a'. Что касается p1 (x), он обозначает предикат; пока он возвращает Trueили False, неважно, вызов функции или выражение, это нормально.
leo 13.03.2015 16:16:23
@ BobStein-VisiBone Да, вы правы! Спасибо :) Для работы многострочного выражения необходимо поместить скобки, как в вашем предложении, или как в моем измененном примере.
leo 14.03.2015 05:16:36
Превосходно. Теперь я удалю все свои комментарии о паренсе.
Bob Stein 14.03.2015 20:48:21
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
49
24.03.2019 04:14:19
Использование контекстных менеджеров - хорошее креативное решение. Я бы порекомендовал добавить немного объяснения и, возможно, ссылку на некоторую информацию о менеджерах контекста, чтобы дать этому посту некоторый, ну, в общем, контекст;)
Will 3.05.2015 09:13:53
Мне не очень нравятся цепочки if / elif, но это самое креативное и практичное из всех решений, которые я видел, используя существующий синтаксис Python.
itsbruce 2.10.2017 08:05:27
Это действительно мило. Одним из предлагаемых улучшений является добавление (публичного) valueсвойства в класс Switch, чтобы вы могли ссылаться на него case.valueвнутри оператора.
Peter 24.01.2019 11:03:48

Мой любимый рецепт Python для switch / case:

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'))
184
16.08.2015 21:58:26
Я считаю, что это более надежный ответ, чем принятый.
cerd 18.08.2015 23:16:50
@ Некоторые пользователи: C требует, чтобы возвращаемое значение было одинаковым для всех случаев. Python нет. Я хотел подчеркнуть эту гибкость Python на тот случай, если у кого-то возникнет ситуация, которая оправдывает такое использование.
ChaimG 1.03.2016 00:17:10
@ Некоторые пользователи: Лично я нахожу {} .get (,) читабельным. Для дополнительной читаемости для начинающих Python вы можете использовать default = -1; result = choices.get(key, default).
ChaimG 1.03.2016 00:19:26
сравнить с 1 строкой c ++result=key=='a'?1:key==b?2:-1
Jasen 23.08.2016 22:01:57
@Jasen можно утверждать , что вы можете сделать это в одной строке Python , а также: result = 1 if key == 'a' else (2 if key == 'b' else 'default'). но читается ли один вкладыш?
ChaimG 24.08.2016 02:57:05
# 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
10
24.10.2015 01:17:46

Я думаю, что лучший способ - использовать идиомы языка 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)

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

5
20.04.2016 14:54:43
Действительно некрасиво ... корпус переключателя так чист при чтении. Не могу понять, почему это не реализовано в Python.
jmcollin92 8.04.2016 13:25:03
@AndyClifton: извините ... пример? Подумайте о том, что каждый раз, когда вам нужно иметь несколько ветвящихся кодов решений, вы можете применить этот метод.
J_Zar 11.04.2016 06:51:37
@ jmcollin92: оператор switch удобен, я согласен. Однако программист имеет тенденцию писать очень длинные операторы и код, который нельзя использовать повторно. Способ, который я описал, более чистый для тестирования и более пригодный для повторного использования, ИМХО.
J_Zar 11.04.2016 06:53:38
@J_Zar: ре. Мой запрос на пример: да, я понимаю, но я изо всех сил пытаюсь поместить это в контекст большего куска кода. Не могли бы вы показать, как я мог бы использовать это в реальной ситуации?
Andy Clifton 11.04.2016 09:14:07
@AndyClifton: извините, я опоздал, но я опубликовал несколько примеров.
J_Zar 20.04.2016 14:56:11

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

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')
""")
4
25.05.2016 22:56:30

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

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

Это может быть использовано с @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

Хорошей новостью является то, что это уже было сделано в модуле NeoPySwitch . Просто установите используя pip:

pip install NeoPySwitch
5
23.05.2017 12:26:42

После того, как я прочитал ответ, я был в замешательстве, но это все прояснило

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

Этот код аналогичен:

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

Проверьте источник для получения дополнительной информации о сопоставлении словаря с функциями.

4
19.06.2017 11:49:28

Оператор Switch является просто синтаксическим сахаром для if / elif / else. То, что делает любой управляющий оператор, - это делегирование задания на основе определенного условия - выполняется путь решения. Для переноса этого в модуль и возможности вызова задания на основе его уникального идентификатора можно использовать наследование и тот факт, что любой метод в Python является виртуальным, чтобы обеспечить производную реализацию конкретной задачи, как специфический обработчик "case"

#!/usr/bin/python

import sys

class Case(object):
    """
        Base class wich specifies the interface for "case" handler.
        The all required arbitrary arguments inside "execute" method will be
        provided through the derived class
        specific constructor

        @note in python, all class methods are virtual
    """
    def __init__(self, id):
        self.id = id

    def pair(self):
        """
            Pairs the given id of the "case" with
            the instance on which "execute" will be called
        """
        return (self.id, self)

    def execute(self):#base class virtual method that needs to be overrided
        pass

class Case1(Case):
    def __init__(self, id, msg):
        self.id = id
        self.msg = msg
    def execute(self):#override the base class method
        print("<Case1> id={}, message: \"{}\"".format(str(self.id), self.msg))

class Case2(Case):
    def __init__(self, id, n):
        self.id = id
        self.n = n
    def execute(self):#override the base class method
        print("<Case2> id={}, n={}.".format(str(self.id), str(self.n)))
        print("\n".join(map(str, range(self.n))))



class Switch(object):
    """
        The class wich delegates the jobs
        based on the given job id
    """
    def __init__(self, cases):
        self.cases = cases#dictionary: time complexitiy for access operation is 1
    def resolve(self, id):

        try:
            cases[id].execute()
        except KeyError as e:
            print("Given id: {} is wrong!".format(str(id)))



if __name__ == '__main__':

    # Cases
    cases=dict([Case1(0, "switch").pair(), Case2(1, 5).pair()])

    switch = Switch(cases)

    # id will be dynamically specified
    switch.resolve(0)
    switch.resolve(1)
    switch.resolve(2)
-2
6.08.2017 12:44:25

Я просто собираюсь бросить свои два цента здесь. Причина, по которой в Python нет оператора case / switch, заключается в том, что Python следует принципу «есть только один правильный способ сделать что-то». Очевидно, что вы могли бы придумать различные способы воссоздания функциональности switch / case, но Pythonic способ сделать это - конструкция if / elif. т.е.

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

Я просто чувствовал, что PEP 8 заслуживает одобрения. Одна из прекрасных особенностей Python - это его простота и элегантность. Это в значительной степени основано на принципах, заложенных в PEP 8, в том числе «Есть только один правильный способ сделать что-то»

23
14.08.2017 22:19:38
Так почему же в Python есть циклы for и while? Все, что вы можете сделать с помощью цикла for, вы можете реализовать с помощью цикла while.
itsbruce 1.10.2017 10:01:28
Правда. Переключатель / регистр слишком часто используются начинающими программистами. То, что они действительно хотят, это шаблон стратегии .
user228395 6.10.2017 12:34:27
Похоже, Python хотел бы, чтобы это был Clojure
T.W.R. Cole 16.02.2018 05:17:02
@TWRCole Я так не думаю, Python делал это первым. Python существует с 1990 года, а Clojure - с 2007 года.
Taylor 9.05.2018 16:52:28
Есть только один правильный способ сделать что-то. Python 2.7 или Python 3? Лол.
T.W.R. Cole 23.08.2018 19:09:34

Я сделал реализацию Switch Case, которая не совсем использует ifs внешне (она все еще использует if в классе).

class SwitchCase(object):
    def __init__(self):
        self._cases = dict()

    def add_case(self,value, fn):
        self._cases[value] = fn

    def add_default_case(self,fn):
        self._cases['default']  = fn

    def switch_case(self,value):
        if value in self._cases.keys():
            return self._cases[value](value)
        else:
            return self._cases['default'](0)

Используйте это так:

from switch_case import SwitchCase
switcher = SwitchCase()
switcher.add_case(1, lambda x:x+1)
switcher.add_case(2, lambda x:x+3)
switcher.add_default_case(lambda _:[1,2,3,4,5])

print switcher.switch_case(1) #2
print switcher.switch_case(2) #5
print switcher.switch_case(123) #[1, 2, 3, 4, 5]
2
31.08.2017 12:36:18
Получите прирост производительности, если вы замените if value in keysего блоком try-Кроме.
iBug 27.03.2018 09:00:09