Python lambda 绑定到本地值
Posted
技术标签:
【中文标题】Python lambda 绑定到本地值【英文标题】:Python lambda's binding to local values 【发布时间】:2012-05-14 05:32:15 【问题描述】:以下代码吐出两次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 函数并让它在使用时保留正确的引用?我对这种行为感到非常惊讶,因为我不希望有垃圾收集器的语言会出现这种情况。
【问题讨论】:
我见过这个问题的几个变种。有人需要汇总所有这些,将标题更改为令人难忘的内容,然后告诉互联网谷歌 啊,给你,几乎是重复的:lexical-closures-in-python @shep,是的,我无法正确表达它,我试图复制 lua 的工作方式。 Python 的奇怪之处在于循环变量在循环之后仍然存在,因此在循环内定义并引用循环变量的函数在循环外调用时不会抛出NameError
。在大多数其他语言中,循环变量只能在循环本身内访问。
【参考方案1】:
将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
【讨论】:
很有趣,请您详细说明一下机制的直觉。 一个更明显的替代方法是使用functools.partial
或使用单独的辅助函数来创建闭包(def make_closure(v): return lambda: pv(v)
,您可以将其放入test
)。
所以我的真实代码应该像lambda par1, par2 = l3_e : self.parse_l4(par1, par2)
?
如果v
本身就是一个引用(而不是int
),那么我认为它不会是一个“深层”副本?
@AaronMcDaid 它不会。【参考方案2】:
lambda 的闭包持有对正在使用的变量的引用,而不是它的值,所以如果变量的值稍后发生变化,闭包中的值也会发生变化。也就是说,闭包变量的值是在调用函数时解析的,而不是在创建函数时解析的。 (Python 在这里的行为在函数式编程世界中并不少见,因为它的价值。)
有两种解决方案:
使用默认参数,在定义时将变量的当前值绑定到本地名称。 lambda v=v: pv(v)
使用双 lambda 并立即调用第一个。 (lambda v: lambda: pv(v))(v)
【讨论】:
我喜欢第二种方法,因为我想冻结在当前值的变量不一定是 lambda 的参数。 @g.a.该值不需要已经是第一个技术工作的 lambda 参数。相反,真的以上是关于Python lambda 绑定到本地值的主要内容,如果未能解决你的问题,请参考以下文章
SWIG Python 绑定到本地代码不适用于 OpenCV 2.1
基于 XAML 绑定到区域性名称属性,使用不同区域性在 WPF GridView 中本地化货币值