Python - 字典中的函数分配不正确

时间:2015-02-13 01:16:12

标签: python dictionary lambda

我试图通过使用字典来包含和索引与某些计算相关的函数来简化我的一些代码。我遇到了一个问题,即字典中的函数变得混乱并且行为不可预测。

这说明了我遇到的问题......

def y_at_x_first(x):
    return x * 1.0

def y_at_x_second(x):
    return x * 2.0

things = {
    'first': {
        'y_at_x': lambda x: y_at_x_first(x)
    },
    'second': {
        'y_at_x': lambda x: y_at_x_second(x)
    },
}

for thing in things:
    # Add a new function that makes use of the first
    things[thing]['y2_at_x'] = lambda x: things[thing]['y_at_x'](x)


numbers = list(range(5))

print('first',
      list(map(things['first']['y_at_x'], numbers)),
      ' = ',
      list(map(things['first']['y2_at_x'], numbers)))

print('second',
      list(map(things['second']['y_at_x'], numbers)),
      ' = ',
      list(map(things['second']['y2_at_x'], numbers)))

我期待它打印出来:

first [0.0, 1.0, 2.0, 3.0, 4.0]  =  [0.0, 1.0, 2.0, 3.0, 4.0]
second [0.0, 2.0, 4.0, 6.0, 8.0]  =  [0.0, 2.0, 4.0, 6.0, 8.0]

但它实际打印的是随机选择:

first [0.0, 1.0, 2.0, 3.0, 4.0]  =  [0.0, 2.0, 4.0, 6.0, 8.0]
second [0.0, 2.0, 4.0, 6.0, 8.0]  =  [0.0, 2.0, 4.0, 6.0, 8.0]

first [0.0, 1.0, 2.0, 3.0, 4.0]  =  [0.0, 1.0, 2.0, 3.0, 4.0]
second [0.0, 2.0, 4.0, 6.0, 8.0]  =  [0.0, 1.0, 2.0, 3.0, 4.0]

它实际上是一个随机选择,多次运行代码并且它会发生变化(我假设它与未被排序的字典有关,因此随机性来自于此)。 我认为这必须是我的参考文献的问题所以我尝试用copy.deepcopy()包围所有函数引用,但问题仍然存在。

非常感谢任何帮助。 我知道其他方法可以实现我想要实现的目标,但我想知道这是我的理解或Python的问题。 有趣的是,在Python3中,结果是随机的;在Python2中,结果始终是第二个选项(将4.0作为元素4)。

2 个答案:

答案 0 :(得分:2)

问题在于此代码:

for thing in things:
    # Add a new function that makes use of the first
    things[thing]['y2_at_x'] = lambda x: things[thing]['y_at_x'](x)

您在lambda函数中使用thing但Python赢了"存储" lambda函数中的这个值以供日后使用。例如,如果我将thing更改为其他内容,则在调用该函数时,它将thing使用该值:

>>> thing = 'foo'
>>> things['first']['y2_at_x'](3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in <lambda>
KeyError: 'foo'

您可以确保按预期使用thing来避免此问题(例如,在for循环运行时,它将具有适当的值):

for thing in things:
    # Add a new function that makes use of the first
    f = things[thing]['y_at_x']  # thing will have the value of the key here
    things[thing]['y2_at_x'] = lambda x: f(x)

通过访问循环中的函数对象,它可以清除您正在使用的对象,然后您可以在lambda函数中调用它。

您所看到的随机性确实与字典无序这一事实有关,因此不知道firstsecond是否优先。

答案 1 :(得分:0)

正如Semeon所说:

  

你正在使用lambda函数中的东西,但Python赢了&#34;存储&#34;   lambda函数中的这个值以供日后使用。

所以我需要一种在lambda函数中设置thing值的方法。 通过使用exec()函数包装赋值,我可以使用Python&#39;键入&#39; lambda函数中的正确字符串。

for thing in things:
    exec("things[thing]['y2_at_x'] = lambda x: things['"+thing+"']['y_at_x'](x)")

这解决了这个问题,但我觉得使用exec语句并不是最优雅的解决方案。如果有人有更好的解决方案,我很乐意听到它。