在定义函数但未调用函数时,Python解释器是否解析变量引用?

时间:2015-12-19 06:10:35

标签: python function garbage-collection

首先,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()

尝试运行f1f2

>>> f1()
[0, 1, 0]
>>> f2
[0, 1, 2]

这表明先前由vals引用的对象由f1f2共享,而不是在执行func后进行垃圾回收。

other_valsother引用的对象是否会被垃圾回收?我认同。但是Python如何决定不垃圾收集vals

假设1

Python解释器将解析func1func2中的变量名来计算函数内部的引用,并将[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]的引用计数?

哪种假设是正确的?

1 个答案:

答案 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__?

闭包机制确保解释器在返回的函数对象valsfunc1中存储对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]