python中奇怪的闭包行为

时间:2012-06-19 21:37:08

标签: python closures

我有一个简单的代码:

def get():
    return [lambda: i for i in [1, 2, 3]]

for f in get():
    print(f())

根据我的python知识预期,输出为3 - 整个列表将包含i的最后一个值。但这是如何在内部工作的?

AFAIK,python变量只是对象的引用,因此第一个闭包必须包含对象的第一个i引用 - 这个对象肯定是1,而不是3 O_O。如何发生python闭包包含变量本身而不是对象这个变量引用?它是否将变量名称保存为纯文本,某些“变量引用”还是什么?

3 个答案:

答案 0 :(得分:12)

正如@ thg435指出的那样,lambda不会在那一刻封装值,而是封装范围。你可以用太小的方法解决这个问题:

lambda默认参数“hack”

[ lambda v=i: v for i in [ 1, 2, 3 ] ]

或使用functools.partial

from functools import partial
[ partial(lambda v: v, i) for i in [ 1, 2, 3 ] ]

基本上,您必须将范围移动到您正在创建的功能的本地。一般来说,我更喜欢使用partial,因为你可以传递一个可调用的,以及任何args和kargs来创建一个具有适当闭包的callable。在内部,它正在包装您的原始可调用对象,因此范围会为您移动。

答案 1 :(得分:10)

闭包不是指变量,而是指范围。由于其范围中i的最后一个值为'3',因此所有三个闭包都返回相同的值。要“锁定”变量的当前值,请为其创建一个新范围:

def get() : return [ (lambda x: lambda: x)(i) for i in [ 1, 2, 3 ] ]
for f in get() : print( f() )

答案 2 :(得分:4)

每个lambda实际上指的是同一个i,它是由列表推导创建的变量。在列表推导终止后,i维护它所分配的最终元素的值,直到它超出范围(通过将其封装在函数中并将其返回,即lambda来阻止它。 })。正如其他人所指出的那样,闭包不会保留值的副本,而是维护对其范围内定义的变量的引用。