Как сопоставить ряд условий как ключей в словаре?
Я знаю, что вы можете использовать словарь в качестве альтернативы оператору switch, например:
def printMessage(mystring):
# Switch statement without a dictionary
if mystring == "helloworld":
print "say hello"
elif mystring == "byeworld":
print "say bye"
elif mystring == "goodafternoonworld":
print "good afternoon"
def printMessage(mystring):
# Dictionary equivalent of a switch statement
myDictionary = {"helloworld": "say hello",
"byeworld": "say bye",
"goodafternoonworld": "good afternoon"}
print myDictionary[mystring]
Однако, если используются условия, отличные от равенства (==), которые возвращают true из false, они не могут быть отображены так легко:.
if i > 0.5:
print "greater than 0.5"
elif i == 5:
print "it is equal to 5"
elif i > 5 and i < 6:
print "somewhere between 5 and 6"
Вышеуказанное не может быть напрямую преобразовано в пару слов-словаря слова:
# this does not work
mydictionary = { i > 0.5: "greater than 0.5" }
Лямбда может использоваться, поскольку она хешируется, но единственный способ получить полученную строку из карты - передать тот же лямбда-объект в словарь, а не когда оценка лямбда истинна:
x = lambda i: i > 0.5
mydictionary[x] = "greater than 0.5"
# you can get the string by doing this:
mydictionary[x]
# which doesnt result in the evaluation of x
# however a lambda is a hashable item in a dictionary
mydictionary = {lambda i: i > 0.5: "greater than 0.5"}
Кто-нибудь знает о методе или методе создания сопоставления между оценкой лямбда и возвращаемым значением?
(это может быть похоже на сопоставление шаблонов в функциональном языке)
Ответы
Ответ 1
Ваши условия носят последовательный характер; вы хотите протестировать один за другим, а не отображать небольшое количество ключей на значение здесь. Изменение порядка условий может изменить исход; значение 5
приводит к "greater than 0.5"
в вашем примере, а не "it is equal to 5"
.
Используйте список кортежей:
myconditions = [
(lambda i: i > 0.5, "greater than 0.5"),
(lambda i: i == 5, "it is equal to 5"),
(lambda i: i > 5 and i < 6, "somewhere between 5 and 6"),
]
после чего вы можете получить доступ к каждому из них по очереди, пока не найдете:
for test, message in myconditions:
if test(i):
return message
Повторный заказ тестов изменит результат.
Словарь работает для вашего первого примера, потому что существует простой тест равенства против множества статических значений, который оптимизируется с помощью словаря, но здесь нет таких простых равенств.
Ответ 2
Вы не можете использовать словарь для сопоставления произвольных условностей, поскольку более одного из них могут быть истинными одновременно. Вместо этого вам нужно каждый раз оценивать каждый из них и выполнять связанный код при первом появлении истинного. Вот схема одного из способов формального реализации чего-то подобного, что даже позволяет эквивалент случая default:
.
from collections import namedtuple
Case = namedtuple('Case', ['condition', 'code'])
cases = (Case('i > 0.5',
"""print 'greater than 0.5'"""),
Case('i == 5',
"""print 'it is equal to 5'"""),
Case('i > 5 and i < 6',
"""print 'somewhere between 5 and 6'"""))
def switch(cases, **namespace):
for case in cases:
if eval(case.condition, namespace):
exec(case.code, namespace)
break
else:
print 'default case'
switch(cases, i=5)
Вывод:
greater than 0.5
Ответ 3
Не имеет прямого отношения, но я часто использую парадигму, подобную приведенному ниже примеру, для замены каскадного ifs на поиск в dictionaruy.
def multipleifs(a=None,b=None,c=None,d=None,e=None):
""" Func1 with cascaded if
>>> multipleifs(10,20,30,40,50)
160
"""
x=10
if a:
x += 10
if b:
x += 20
if c:
x += 30
if d:
x += 40
if e:
x += 50
return x
def dictif(a=None,b=None,c=None,d=None,e=None):
""" Func2 with dictionary replacing multiple ifs
>>> dictif(10,20,30,40,50)
160
"""
x, mydict = 10, dict(enumerate([10,20,30,40,50]))
for count, item in enumerate([a,b,c,d,e]):
if item: x += mydict.get(count,0)
return x