Переключить в Python - PullRequest
       1

Переключить в Python

12 голосов
/ 17 января 2012

Я попытался сделать оператор, похожий на 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, и они следуют этой структуре.Кто-нибудь может мне помочь?

Ответы [ 7 ]

18 голосов
/ 17 января 2012

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

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

Примечание: Мне сообщили в комментариях, что независимо от причины, по которой Гвидо вообще не создал переключатель, 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
6 голосов
/ 17 января 2012

Нет ничего плохого в длинных 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__()

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

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)
1 голос
/ 17 января 2012

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

0 голосов
/ 02 июля 2017

Хотя в if..ese нет ничего плохого, я нахожу «switch in 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).Во-вторых, каждый случай может содержать только одну функцию, которая будет выполнена при найденном случае.

0 голосов
/ 17 января 2012

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

Самая большая проблема с тем, как вы это делаете, заключается в том, что 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, чтобы изменить совершенно другую переменную, а затем мы должны выяснить, как ее получитьверните 0, чтобы c не было изменено.Uuuuuugly.

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

0 голосов
/ 17 января 2012

С помощью метода «get» вы можете получить тот же эффект, что и «switch..case» в C.

Пример Marcin:

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
0 голосов
/ 17 января 2012

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

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

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