Словарь Python вместо switch / case - PullRequest
       14

Словарь Python вместо switch / case

2 голосов
/ 08 февраля 2010

Я недавно узнал, что в python нет оператора switch / case. Я читал об использовании словарей вместо них, например, вот так:

values = { 
     value1: do_some_stuff1, 
     value2: do_some_stuff2, 
     valueN: do_some_stuffN,
}
values.get(var, do_default_stuff)()

Что я не могу понять, так это как применить это для проверки дальности. Поэтому вместо того, чтобы что-то делать, если value1=4 скажем, делать что-то, если value1<4. Так что-то вроде этого (что, я знаю, не работает):

values = { 
     if value1 <val: do_some_stuff1, 
     if value2 >val: do_some_stuff2, 
}
values.get(var, do_default_stuff)()

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

if sep_ang(val1,val2,X,Y)>=ROI :
    main.removeChild(source)
elif sep_ang(val1,val2,X,Y)<=5.0:
    integral=float(spectrum[0].getElementsByTagName("parameter")[0].getAttribute("free"))
    index=float(spectrum[0].getElementsByTagName("parameter")[0].getAttribute("free"))              
    print name,val1,val2,sep_ang(val1,val2,X,Y),integral,index
    print >> reg,'fk5;point(',val1,val2,')# point=cross text={',name,'}'
else:
    spectrum[0].getElementsByTagName("parameter")[0].setAttribute("free","0") #Integral
    spectrum[0].getElementsByTagName("parameter")[1].setAttribute("free","0") #Index
    integral=float(spectrum[0].getElementsByTagName("parameter")[0].getAttribute("free"))
    index=float(spectrum[0].getElementsByTagName("parameter")[0].getAttribute("free"))
    print name,val1,val2,sep_ang(val1,val2,X,Y),integral,index
    print >> reg,'fk5;point(',val1,val2,')# point=cross text={',name,'}'

Это занимает около 5 минут для проверки около 1500 значений переменной sep_ang. Где, как будто я не хочу использовать setAttribute () для изменения значений в моем XML-файле на основе значения sep_ang, я использую это просто, если еще:

if sep_ang(val1,val2,X,Y)>=ROI :
    main.removeChild(source)
else:
    print name,val1,val2,ang_sep(val1,val2,X,Y);print >> reg,'fk5;point(',val1,val2,')# point

Что занимает всего ~ 30сек. Опять же, я знаю, что вполне вероятно, что добавление этого оператора elif и изменение значений этого атрибута неизбежно значительно увеличивает время выполнения моего кода, мне просто было любопытно, есть ли способ обойти это.

Edit: Преимущество использования bisect по сравнению с оператором if / elif в моей ситуации состоит в том, что он может проверять значения в некотором диапазоне быстрее, чем использование набора операторов elif?

Кажется, мне все еще нужно использовать выражения elif. Вот так например:

range=[10,100]
options='abc' 
def func(val)
     return options[bisect(range, val)]
if func(val)=a:
     do stuff
elif func(val)=b:
     do other stuff
else:
     do other other stuff

Итак, мое утверждение elif проверяет только одно значение.

Большое спасибо за помощь, она очень ценится.

Ответы [ 6 ]

10 голосов
/ 08 февраля 2010

Словарь является неправильной структурой для этого. Примеры bisect показывают пример такого типа проверки диапазона.

4 голосов
/ 08 февраля 2010

Хотя словарный подход хорошо работает для отдельных значений, если вам нужны диапазоны, if ... else if ... else if, вероятно, самый простой подход.

Если вы ищете одно значение, это хорошее совпадение со словарем - поскольку это то, для чего предназначены словари, - но если вы ищете диапазон, он не работает. Вы можете сделать это с dict используя что-то вроде:

values = {
    lambda x: x < 4: foo,
    lambda x: x > 4: bar
}

, а затем переберите все пары ключ-значение в словаре, передавая ключ значения и выполняя значение как функцию, если функция ключа возвращает true.

Однако это не даст вам никакой выгоды по сравнению с рядом операторов if, и его будет сложнее поддерживать и отлаживать. Так что не делайте этого, а просто используйте if.

3 голосов
/ 08 февраля 2010

В этом случае вы бы использовали if / then / else. Вы также не можете сделать это с помощью переключателя.

Идея оператора switch состоит в том, что у вас есть значение V, которое вы проверяете на идентичность с N возможными результатами. Вы можете сделать это с помощью if-конструкции - однако для этого потребуется в среднем время выполнения O (N). Переключатель дает вам постоянную O (1) каждый раз.

Это явно невозможно для диапазонов (так как они не легко хэшируемы), и поэтому вы используете if-конструкции для этих случаев.

Пример

if value1 <val: do_some_stuff1()
elif value2 >val: do_some_stuff2()

Обратите внимание, что это на самом деле меньше, чем пытаться использовать словарь.

2 голосов
/ 08 февраля 2010

dict не для этого (и не переключатель!).

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

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

0 голосов
/ 09 февраля 2010

Наконец-то разобрался, что делать!

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

range=[10,100]
options='abc' 
def func(val)
     choose=str(options[bisect(range,val)])
     exec choose+"()"
def a():
      do_stuff
def b():
      do_other_stuff
def c():
      do_other_other stuff

Мало того, что он работает, но он работает почти так же быстро, как мой оригинальный 4-строчный код, где я не изменяю значения вещей!

0 голосов
/ 08 февраля 2010

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

obsure_switch = {
     lambda x: 1<x<6 : some_function,
     ...
}

[action() for condition,action in obscure_switch.iteritems() if condition(var)]
...