13

我有以下简单的代码:

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 闭包如何包含变量本身而不是对象这个变量引用?它是否将变量名称保存为纯文本,一些“对变量的引用”或什么?

4

3 回答 3

13

正如@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 来创建具有适当闭包的可调用对象。在内部,它包装了您的原始可调用对象,因此范围为您转移。

于 2012-06-19T21:45:34.793 回答
10

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

def get() : return [ (lambda x: lambda: x)(i) for i in [ 1, 2, 3 ] ]
for f in get() : print( f() )
于 2012-06-19T21:41:06.880 回答
4

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

于 2012-06-19T21:46:51.503 回答