Python:在重新分配外部函数后,闭包如何继续存在?

标签 python function memory closures

我学习了 Python 中的闭包,我对这个概念的理解非常好。在闲置时,我想过如果我重新分配封闭函数然后尝试调用封闭函数会发生什么:

>>> def outer_function(hello):
        message = hello
        def inner_function():
            print(message)
        return inner_function

>>> function = outer_function("hello")

>>> function()
hello

>>> def outer_function():
    print("hi")

>>> function()
hello

我认为这很有趣,但我意识到我对内存中的闭包会发生什么等问题没有足够的了解。有人可以解释一下我如何在重新分配后调用 inner_function outer_function 的?

最佳答案

在 CPython 中(即大多数人认为只是“Python”的用 C 编写的引用实现),词法闭包被实现为“平面闭包”(参见 PEP 227),它使用单元格对象引用而不是搜索链接运行时框架对象(嵌套范围)的列表。这允许在返回闭包函数时进行快速查找并改进垃圾收集。

outer_function 中的字节码专门用于访问栈帧中的单元格对象,而不是直接引用message 对象。解释器在为调用设置堆栈帧时知道这一点,因为代码对象将此变量定义为单元格变量:

>>> outer_function.__code__.co_cellvars
('message',)

inner_function 中的字节码也为 message 的值取消引用单元格对象,但由于它不是对象的源,因此它被归类为自由变量:

>>> type(outer_function.__code__.co_consts[1])
<class 'code'>
>>> outer_function.__code__.co_consts[1].co_name
'inner_function'
>>> outer_function.__code__.co_consts[1].co_freevars
('message',)

每个被实例化的 inner_function 函数对象都有一个 __closure__ 元组,它引用包含的自由变量的单元格。例如:

>>> function = outer_function('hello')
>>> type(function.__closure__[0])
<class 'cell'>
>>> function.__closure__[0].cell_contents
'hello'

__closure__ 元组中的单元格在调用 function 时加载到栈帧中。

这个单元格元组使它变平。无论您将作用域嵌套多深,__closure__ 总是会传播所有需要的单元格。例如:

def a():
    x = 1
    def b():
        def c():
            def d():
                x
            print('d.__closure__[0].cell_contents:',
                  d.__closure__[0].cell_contents)
        print('c.__closure__[0].cell_contents:',
              c.__closure__[0].cell_contents)
        c()
    print('b.__closure__[0].cell_contents:',
          b.__closure__[0].cell_contents)
    b()

>>> a()
b.__closure__[0].cell_contents: 1
c.__closure__[0].cell_contents: 1
d.__closure__[0].cell_contents: 1

函数 bc 不直接引用 x,但它们必须传播内部函数 d 的单元格 来引用它。


以上检查依赖于 CPython 实现细节。在 Python 3.3+ 中,您可以改为调用 inspect.getclosurevars检查闭包变量。例如:

import inspect

def outer_function(hello):
    message = hello
    def inner_function():
        print(message)
    return inner_function

>>> function = outer_function('hello')
>>> inspect.getclosurevars(function)
ClosureVars(nonlocals={'message': 'hello'},
            globals={},
            builtins={'print': <built-in function print>},
            unbound=set())

关于Python:在重新分配外部函数后,闭包如何继续存在?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44006489/

相关文章:

python - 创建一个以列表或整数作为参数的函数 (Python)

php - 可以用MySQL数据库代替变量吗?

c - 如何使用 C 有选择地将变量存储在内存段中?

c - 即使没有内存泄漏,Valgrind 也会出现无效读取错误

python - 获取 OSError : [Errno 16] Device or resource busy: ' when using tf. keras.models.Sequential.fit_generator

Python 查找对,相同的值

python - 在 Python 中线程化相同的函数以获得不同的结果

c++ - 如何测量C++中的内存分配时间?

python - python webpy框架中缓存控制的正确方法是什么

python - 在 Python 中的列表上循环函数