nag*_*hna 3 python callstack nested-function
我正在上编程语言课.导师正在解释有关呼叫堆栈的问题.我怀疑导师无法正确解释.如果函数(func1)返回嵌套函数(比如func2并让func2使用func1中定义的变量).我们将返回的值保存在某个变量中,比如returnFunc.在func1中的return语句完成后,func1将退出.并且func1的堆栈帧应该从调用堆栈中弹出.我们现在在代码中的其他地方调用returnFunc.但是,returnFunc使用func1本地的变量,其堆栈帧不再存在于调用堆栈中.这是如何运作的.
示例python代码:
def func1():
a = 3;
def func2():
print(a)
return func2
returnedFunc = func1()
returnedFunc()
Run Code Online (Sandbox Code Playgroud)
这段代码正确打印3.然而我期待一些垃圾值,因为func1在调用堆栈上不再存在
func1内部函数func2使用的外部函数中的变量及其值在定义内部函数时被"打包" func2,并且func2在func1返回时会出现"词汇环境" .func2是所谓的闭包(文章顶部给出的示例与您的非常相似,并将其扩展一点).你正确的是,当该函数返回时,gets 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)
Run Code Online (Sandbox Code Playgroud)
正如你所料,
>>> inner3(1)
4
>>> inner5(1)
6
Run Code Online (Sandbox Code Playgroud)
您可以使用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())
Run Code Online (Sandbox Code Playgroud)
但是,如果两个闭包使用相同的非局部变量(如示例中所示),则变量将绑定到同一位置.考虑这种情况(来自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()
Run Code Online (Sandbox Code Playgroud)
调用函数f2并f3建议它们使用相同的值a:
>>> f2(), f2(), f3(), f3()
(4, 5, 4, 3)
Run Code Online (Sandbox Code Playgroud)
检查 __closure__每个属性表明确实如此."单元格"(绑定)是相同的,每个"指向"相同的int对象:
>>> f2.__closure__
(<cell at 0x100380fa8: int object at 0x1002739a0>,)
>>> f2.__closure__ == f3.__closure__
True
Run Code Online (Sandbox Code Playgroud)
甲cell(的类对象cell)有一个cell_contents属性; 对于f2与f3该cell_contents是INT对象.这是两个单元指向同一事物的另一个验证:
>>> f2.__closure__[0].cell_contents is f3.__closure__[0].cell_contents
True
Run Code Online (Sandbox Code Playgroud)
事实上,这两个细胞是相同的:
>>> f2.__closure__[0] is f3.__closure__[0]
True
Run Code Online (Sandbox Code Playgroud)