我有一个简单的代码:
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闭包包含变量本身而不是对象这个变量引用?它是否将变量名称保存为纯文本,某些“变量引用”还是什么?
答案 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
来阻止它。 })。正如其他人所指出的那样,闭包不会保留值的副本,而是维护对其范围内定义的变量的引用。