首先,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
共享,并且在执行 后不会被垃圾回收函数
。
other_vals
和 other
引用的对象会被垃圾回收吗?我想是这样。但是 Python 如何决定不对 vals
进行垃圾回收呢?
假设1
Python 解释器将解析 func1
和 func2
中的变量名称,以找出函数内部的引用,并增加 [0, 0, 0 的引用计数]
减 1,防止在 func
调用后进行垃圾回收。
但如果我这样做
# 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]
的引用计数?
哪个假设是正确的?
最佳答案
您的第一个示例创建了 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__
? .
闭包机制确保解释器在返回的函数对象func1
和func2
中存储对vals
的引用。您的假设 1 是正确的:当 func
返回时,该引用会阻止 vals
被垃圾回收。
在您的第二个示例中,解释器无法在封闭范围中看到对 non_existent_variable
的引用,但这没关系,因为您的假设 2 也是正确的,因此您可以自由使用名称在函数声明时尚未绑定(bind)到对象,只要名称在实际调用函数时位于范围内即可。
“解释器如何知道需要增加代码块 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]
关于python - 当定义函数但未调用函数时,Python 解释器会解析变量引用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34367888/