87

以下代码吐出1两次,但我希望看到0然后1.

def pv(v) :
  print v

x = []
for v in range(2):
  x.append(lambda : pv(v))

for xx in x:
  xx()

我希望 python lambdas 在幕后绑定到局部变量指向的引用。然而,情况似乎并非如此。我在一个大型系统中遇到了这个问题,其中 lambda 正在执行现代 C++ 的等效绑定(例如,'boost::bind'),在这种情况下,您将绑定到智能 ptr 或复制构造 lambda 的副本。

那么,如何将局部变量绑定到 lambda 函数并让它在使用时保留正确的引用?我对这种行为感到非常惊讶,因为我不希望这种行为来自带有垃圾收集器的语言。

4

2 回答 2

135

更改x.append(lambda : pv(v))x.append(lambda v=v: pv(v))

您期望“python lambdas 在幕后绑定到局部变量指向的引用”,但这不是 Python 的工作方式。Python 在调用函数时查找变量名,而不是在创建时查找。使用默认参数是有效的,因为默认参数是在创建函数时评估的,而不是在调用它时。

这对 lambda 来说并没有什么特别之处。考虑:

x = "before foo defined"
def foo():
    print x
x = "after foo was defined"
foo()

印刷

after foo was defined
于 2012-05-04T16:36:46.487 回答
37

lambda 的闭包持有对正在使用的变量的引用,而不是它的值,所以如果变量的值稍后发生变化,闭包中的值也会发生变化。也就是说,闭包变量的值是在调用函数时解析的,而不是在创建函数时解析的。(Python 在这里的行为在函数式编程世界中并不罕见,因为它的价值。)

有两种解决方案:

  1. 使用默认参数,在定义时将变量的当前值绑定到本地名称。 lambda v=v: pv(v)

  2. 使用双 lambda 并立即调用第一个。(lambda v: lambda: pv(v))(v)

于 2012-05-04T16:41:17.527 回答