Python 如何将一系列条件映射为字典中的键?

Python 如何将一系列条件映射为字典中的键?,python,python-2.7,dictionary,lambda,Python,Python 2.7,Dictionary,Lambda,我知道您可以使用字典代替switch语句,例如: def printMessage(mystring): # Switch statement without a dictionary if mystring == "helloworld": print "say hello" elif mystring == "byeworld": print "say bye" elif mystring == "goodafternoonwo

我知道您可以使用字典代替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"
可以使用lambda,因为它可以散列,但从映射中获取结果字符串的唯一方法是将相同的lambda对象传递到字典中,而不是在lambda的计算为true时:

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"}
有人知道在lambda求值和返回值之间创建映射的技术或方法吗?
(这可能类似于函数式语言中的模式匹配)

您的条件本质上是连续的;您希望一个接一个地进行测试,而不是将少量键映射到此处的值。改变条件的顺序可能会改变结果;值
5
会导致样本中
大于0.5“
,而不是
“它等于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"),
]
重新安排测试将改变结果


字典适用于您的第一个示例,因为字典优化了针对多个静态值的简单等式测试,但这里没有此类简单等式。

您不能使用字典映射任意条件,因为其中多个条件可能同时为真。相反,您需要按顺序计算每个代码,并在第一次遇到真正的代码时执行关联的代码。下面概述了一种正式实现类似的功能的方法,它甚至允许使用相当于
default:
case的功能

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)

不直接相关,但我经常使用类似于下面示例的范例,用字典查找替换级联ifs

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

“但是很明显,只有当lambda本身被传递时才有可能,而不是当lambda的评估为真时。”对不起,你能重新表述一下吗?我不明白你的意思。@Kevin好的,我刚刚编辑了答案以澄清这一点。括号不是必需的,但这正是我要发布的内容。@FunkySayu:需要它们将lambda和消息分组为元组。不这样做需要笨拙的循环结构。哦,好吧,我的错,我还以为你用的是字典而不是元组列表呢。顺便说一下,如果没有匹配的条件,您可以在末尾添加return语句?是的,如果没有匹配的条件,您可以添加额外的默认return。或者,您可以根据用例引发一个异常。@MartijnPieters好的,这是有意义的——按顺序性质,您的意思是,条件本身不是互斥的,可以匹配多个条件,从而产生多对一关系,因此映射的使用是无效的。
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)
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