Python lambda 与本地值的绑定

下面的代码吐出 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 + + 的绑定(例如,‘ ost: : bind’) ,在这种情况下,您将绑定到一个智能 ptr 或者复制构造一个 lambda 的副本。

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

35890 次浏览

Change x.append(lambda : pv(v)) to x.append(lambda v=v: pv(v)).

You expect "python lambdas to bind to the reference a local variable is pointing to, behind the scene", but that is not how Python works. Python looks up the variable name at the time the function is called, not when it is created. Using a default argument works because default arguments are evaluated when the function is created, not when it is called.

This is not something special about lambdas. Consider:

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

prints

after foo was defined

The lambda's closure holds a reference to the variable being used, not its value, so if the value of the variable later changes, the value in the closure also changes. That is, the closure variable's value is resolved when the function is called, not when it is created. (Python's behavior here is not unusual in the functional programming world, for what it's worth.)

There are two solutions:

  1. Use a default argument, binding the current value of the variable to a local name at the time of definition. lambda v=v: pv(v)

  2. Use a double-lambda and immediately call the first one. (lambda v: lambda: pv(v))(v)