我正在上课程编程语言。导师正在解释有关呼叫堆栈的问题。我怀疑导师无法正确解释。如果函数(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()
这段代码正确打印3.然而我期待一些垃圾值,因为func1在调用堆栈上不再存在
答案 0 :(得分:6)
变量&内部函数func1
使用的外部函数func2
中的值在定义内部函数时与func2
“打包”,并且“词法环境”附带{{1}当func2
返回时。 func1
是所谓的closure(文章顶部给出的示例与您的非常相似,并稍微扩展一下)。当该函数返回时,func2
的{{1}}副本从堆栈中弹出是正确的,但返回的func1
具有a
与{{1}的绑定}},将在通过func2
调用时使用。 Python比将a
绑定到即将成为垃圾的东西更聪明:)
为了说明,让我们使用一个稍微复杂的例子:
3
正如您所料,
returnedFunc()
您可以使用a
检查闭包的绑定。请注意,每个闭包都有自己的def outer(x):
def inner(y):
return x+y
return inner
inner3 = outer(3)
inner5 = outer(5)
:
>>> inner3(1)
4
>>> inner5(1)
6
但是,如果两个闭包使用相同的非局部变量(如示例中所示),则变量将绑定到同一位置。考虑这种情况(来自OP的评论):
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())
表明它们使用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
属性表明确实如此。 “单元格”(绑定)是相同的,每个“指向”相同的int对象:
a
>>> f2(), f2(), f3(), f3()
(4, 5, 4, 3)
个对象(类__closure__
)具有>>> f2.__closure__
(<cell at 0x100380fa8: int object at 0x1002739a0>,)
>>> f2.__closure__ == f3.__closure__
True
属性;对于cell
和cell
,cell_contents
是int对象。这是两个单元格指向同一事物的另一个验证:
f2
事实上,这两个细胞是相同的:
f3
答案 1 :(得分:0)
func2
仍然包含由编译器分配的scope的副本,因为它使用在该范围内找到的名称。
答案 2 :(得分:0)
Python不同。它有自己的字节码编译器,解释器和堆栈。它不会将C /机器堆栈用于Python代码本身。您的示例也创建了一个新函数。它被分配为堆上的对象,而不是堆栈上的对象。所以它在返回后存在。