【问题标题】:How to map a series of conditions as keys in a dictionary?如何将一系列条件映射为字典中的键?
【发布时间】:2015-06-08 15:05:46
【问题描述】:

我知道您可以使用字典来替代 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" }

可以使用 lambda,因为它是可散列的,但从映射中获取结果字符串的唯一方法是将相同的 lambda 对象传递到字典中,而不是在 lambda 的评估为真时:

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 评估和返回值之间创建映射的技术或方法吗? (这可能类似于函数式语言中的模式匹配)

【问题讨论】:

  • “但显然它只有在 lambda 本身被传递时才有可能,而不是当 lambda 的评估为真时” 抱歉,您能改写一下吗?我不明白你的意思。
  • @Kevin 好的,我刚刚编辑了答案以澄清这一点

标签: python python-2.7 dictionary lambda


【解决方案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

重新排序测试将改变结果。

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

【讨论】:

  • 括号不是必需的,但这就是我要发布的内容。
  • @FunkySayu:他们需要将 lambda 和消息分组到元组中。不这样做就需要笨拙的循环结构。
  • 好吧,我的错,我虽然你使用的是字典而不是元组列表。顺便说一句,您可以在最后添加一个 return 语句以防万一?
  • 是的,如果没有条件匹配,您可以添加额外的默认返回。或者您可以根据用例引发异常。
  • @MartijnPieters 好的,这是有道理的——按顺序的性质,你的意思是条件本身不是互斥的,并且可以匹配多个条件,这给出了多对一的关系,因此使用的地图无效。
【解决方案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】:

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

    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
    

    【讨论】:

      猜你喜欢
      • 2019-09-28
      • 2017-03-06
      • 2021-06-26
      • 1970-01-01
      • 2017-07-07
      • 2018-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多