首先,this post不回答我的问题或给我任何指导来回答我的问题。
我的问题是关于解决非局部变量的机制函数。
代码
# code block 1
def func():
vals = [0, 0, 0]
other_vals = [7, 8, 9]
other = 12
def func1():
vals[1] += 1
print(vals)
def func2():
vals[2] += 2
print vals
return (func1, func2)
f1, f2 = func()
尝试运行f1
,f2
:
>>> f1()
[0, 1, 0]
>>> f2
[0, 1, 2]
这表明先前由vals
引用的对象由f1
和f2
共享,而不是在执行func
后进行垃圾回收。
other_vals
和other
引用的对象是否会被垃圾回收?我认同。但是Python如何决定不垃圾收集vals
?
假设1
Python解释器将解析func1
和func2
中的变量名来计算函数内部的引用,并将[0, 0, 0]
的引用计数增加1,防止它在func
之后进行垃圾回收{1}}致电。
但如果我这样做
# code block 2
def outerfunc():
def innerfunc():
print(non_existent_variable)
f = outerfunc()
未报告任何错误。更多
# code block 3
def my_func():
print(yet_to_define)
yet_to_define = "hello"
作品。
假设2
变量名在运行时动态解析。这使得代码块2和3中的观察结果易于解释,但是解释器如何知道需要增加代码块1中[0, 0, 0]
的引用计数?
哪种假设是正确的?
答案 0 :(得分:3)
您的第一个示例创建了closure;另请参阅Why aren't python nested functions called closures?,Can you explain closures (as they relate to Python)?和What exactly is contained within a obj.__closure__
?。
闭包机制确保解释器在返回的函数对象vals
和func1
中存储对func2
的引用。您的假设1是正确的:当vals
返回时,该引用会阻止func
被垃圾回收。
在您的第二个示例中,解释程序无法在封闭范围内看到对non_existent_variable
的引用,但这很好,因为您的假设2也是正确的,因此您可以自由在函数声明时使用尚未绑定到对象的名称,只要在实际调用函数时名称在范围内即可。
答案"解释器是如何知道需要增加代码块1中[0, 0, 0]
的引用计数?"是闭包机制是解释器在执行函数定义时所做的明确事情,即,当它从脚本中的函数定义创建函数对象时。
每个Python函数对象(普通def
- 样式函数和lambda
)都有一个属性来存储这个闭包信息,Python 2和Python 3之间的细微差别。请参阅链接详细信息的答案的开头,但我在这里会提到Python 3提供了nonlocal
关键字,其工作方式与global
关键字类似:nonlocal
允许您对已关闭的工作进行分配 - 简单变量; J.F. Sebastian's answer有一个简单的例子说明nonlocal
的用法。
请注意,对于嵌套函数,每次调用外部函数时都会处理内部函数定义,这允许您执行以下操作:
def func(vals):
def func1():
vals[1] += 1
print(vals)
def func2():
vals[2] += 2
print(vals)
return func1, func2
f1, f2 = func([0, 0, 0])
f1()
f2()
f1, f2 = func([10, 20, 30])
f1()
f2()
<强>输出强>
[0, 1, 0]
[0, 1, 2]
[10, 21, 30]
[10, 21, 32]