当定义内部函数时,内部函数func2使用的外部函数func1中的变量及其值与func2“打包”,而当定义内部函数时,“词法环境”与func2一起出现func1 返回它。 func2 就是所谓的closure (文章顶部给出的示例与您的非常相似,并对其进行了一些扩展)。你是对的,func1 的a 的副本在该函数返回时从堆栈中弹出,但返回的func2 具有a 到3 的绑定,它将在调用时使用通过returnedFunc()。 Python 比将 a 绑定到即将成为垃圾的东西更聪明:)
为了说明,让我们用一个稍微复杂一点的例子:
def outer(x):
def inner(y):
return x+y
return inner
inner3 = outer(3)
inner5 = outer(5)
正如你所料,
>>> inner3(1)
4
>>> inner5(1)
6
您可以使用inspect.getclosurevars 检查闭包的绑定。请注意,每个闭包都有自己的 'x' 副本:
from inspect import getclosurevars
>>> getclosurevars(inner3)
ClosureVars(nonlocals={'x': 3}, globals={}, builtins={}, unbound=set())
>>> getclosurevars(inner5)
ClosureVars(nonlocals={'x': 5}, globals={}, builtins={}, unbound=set())
但是,如果两个闭包使用相同的非局部变量(如您的示例中所示),则该变量将绑定到相同的位置。考虑这种情况(来自 OP 的评论):
def func1():
a = 3
def func2():
nonlocal a
a += 1
return a
def func3():
nonlocal a
a -= 1
return a
return func2, func3
f2, f3 = func1()
调用函数f2 和f3 表明它们使用相同的a 值:
>>> f2(), f2(), f3(), f3()
(4, 5, 4, 3)
检查每个的__closure__ 属性表明情况确实如此。 “单元”(绑定)是相同的,并且每个“指向”相同的 int 对象:
>>> f2.__closure__
(<cell at 0x100380fa8: int object at 0x1002739a0>,)
>>> f2.__closure__ == f3.__closure__
True
cell 对象(属于cell 类)具有cell_contents 属性;对于f2 和f3,cell_contents 是 int 对象。这是两个单元格指向同一事物的另一个验证:
>>> f2.__closure__[0].cell_contents is f3.__closure__[0].cell_contents
True
其实这两个单元格是一样的:
>>> f2.__closure__[0] is f3.__closure__[0]
True