Переключить на Python

Я попытался сделать оператор switch как python, вместо того, чтобы иметь много операторов if.

Код выглядит следующим образом:

def findStuff(cds):
    L=[]
    c=0
    for i in range(0, len(cds), 3):
        a=differencesTo(cds[i:i+3])
        result = {
            a[2][0]==1: c=i+1,
            a[2][1]==1: c=i+2,
            a[2][2]==1: c=i+3,
            a[1]==1: L.append((cds[i:i+3], a[0], c))
        } 
    return L

Моя проблема в том, что это не работает. (Работает с операторами if, но это, на мой взгляд, будет более красивым).

Я нашел несколько примеров переключателей в Python, и они следуют этой структуре. Кто-нибудь может мне помочь?

Ответ 1

(a) Я не вижу, что не так, если... elif... else

(b) Я предполагаю, что у python нет оператора switch по той же причине, что и Smalltalk: он почти полностью избыточен, и в случае, когда вы хотите включить типы, вы можете добавить соответствующий метод для ваши классы; и аналогичное включение значений должно быть в значительной степени избыточным.

Примечание:. В комментариях сообщается, что независимо от причины, по которой Гвидо не создает коммутатор, PEP, чтобы добавить его, были отклонены на основе того, что поддержка добавления такого выражения крайне ограниченный. См.: http://www.python.org/dev/peps/pep-3103/

(c) Если вам действительно нужно поведение переключения, используйте хеш-таблицу (dict) для хранения вызовов. Структура:

switch_dict = {
    Foo: self.doFoo,
    Bar: self.doBar,
    }

func = switch_dict[switch_var]
result = func() # or if they take args, pass args

Ответ 2

Нет ничего плохого в длинном if:

if switch == 'case0':
   do_case0()
elif switch == 'case1':
   do_case1()
elif switch == 'case2':
   do_case2()
...

Если это слишком длительное время, или если у вас много дел, поместите их в словарь:

switch = {'case0': do_case0, 'case1': do_case1, 'case2': do_case2, ...}
switch[case_variable]()
// Alternative:
(switch[case_variable]).__call__()

Если ваши условия немного сложнее, вам нужно немного подумать о ваших структурах данных. например:.

switch = {(0,21): 'never have a pension',
          (21,50): 'might have a pension',
          (50,65): 'definitely have a pension',
          (65, 200): 'already collecting pension'}
for key, value in switch:
    if key[0] < case_var < key[1]:
        print(value)

Ответ 3

Назначение в Python является выражением и не может быть частью выражения. Кроме того, использование литерала таким образом оценивает все сразу, что, вероятно, не то, что вы хотите. Просто используйте if s, вы не получите никакой удобочитаемости, используя это.

Ответ 4

Я не знаю, какую статью вы нашли, чтобы сделать что-то вроде этого, но это действительно беспорядочно: вся дикция результата будет всегда оценена и вместо того, чтобы выполнять только часть работы (в качестве переключателя /if do), вы будете делать всю работу каждый раз. (даже если вы используете только часть результата).

Действительно, оператор быстрого переключения в Python использует "if":

if case == 1:
  pass
elif case == 2:
  pass
elif case == 3:
  pass
else:
  # default case
  pass

Ответ 5

С методом "get" вы можете иметь тот же эффект, что и "switch..case" в C.

Пример Марсина:

switch_dict = {
    Foo: self.doFoo,
    Bar: self.doBar,
}

func = switch_dict.get(switch_var, self.dodefault)
result = func() # or if they take args, pass args

Ответ 6

Вы можете делать что-то вроде того, чего хотите, но не должны. Тем самым, вот как; вы можете видеть, как это не улучшает ситуацию.

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

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

Итак, здесь мы идем:

def findStuff(cds):

    cases = [ (lambda: a[2][0] == 1, lambda: i + 1),
              (lambda: a[2][1] == 1, lambda: i + 2),
              (lambda: a[2][2] == 1, lambda: i + 3),
              (lambda: a[1] == 1,    lambda: L.append(cds[i:i+3], a[0], c) or 0)
            ]

    L=[]
    c=0
    for i in range(0, len(cds), 3):
        a=differencesTo(cds[i:i+3])
        for condition, action in cases:
            if condition():
                c += action()
                break
    return L

Является ли это более читаемым, чем последовательность операторов if/elif? Nooooooooooooo. В частности, четвертый случай гораздо менее понятен, чем должен быть, потому что нам приходится полагаться на функцию, которая возвращает приращение для c, чтобы изменить совершенно другую переменную, а затем мы должны выяснить, как ее получить верните a 0, чтобы c фактически не был изменен. Uuuuuugly.

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

Ответ 7

Вот еще один способ сделать оператор switch с помощью лямбда-функции и словаря

for caseVar in range(0,3):
    answer=(lambda switch: ({
            0:'a',
            1:'b',
            2:'c',
            }[switch]))(caseVar)
    print("switch",answer)

Ответ 8

Пока нет ничего плохого в if..else, я нахожу "переключатель в Python" все еще интригующим оператором проблемы. На этом я думаю, что вариант Marcin (устаревший) (c) и/или Snim2 второй вариант можно записать более читаемым способом.

Для этого мы можем объявить класс switch и использовать __init__() для объявления case, который мы хотим переключить, в то время как __call__() помогает передать идентификатор, содержащий пары (case, function):

class switch(object):
    def __init__(self, case):
        self._case = case

    def __call__(self, dict_):
        try:
            return dict_[self._case]()
        except KeyError:
            if 'else' in dict_:
                return dict_['else']()
            raise Exception('Given case wasn\'t found.')

Или, соответственно, поскольку класс с двумя методами, один из которых является __init__(), на самом деле не является классом:

def switch(case):
    def cases(dict_):
        try:
            return dict_[case]()
        except KeyError:
            if 'else' in dict_:
                return dict_['else']()
            raise Exception('Given case wasn\'t found.')
    return cases

(обратите внимание: выберите что-то умнее Exception)

С примером

def case_a():
    print('hello world')

def case_b():
    print('sth other than hello')

def default():
    print('last resort')

вы можете позвонить

switch('c') ({
    'a': case_a,
    'b': case_b,
    'else': default
})

который для этого конкретного примера напечатает

в последнюю очередь

Это не ведет себя как C-переключатель, поскольку для разных случаев нет break, потому что каждый случай выполняет только функцию, объявленную для конкретного случая (т.е. break неявно всегда вызывается). Во-вторых, каждый случай может отображать только одну функцию, которая будет выполняться в найденном случае.