gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 02:50:33 25 4
gpt4 key购买 nike

我学习了 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com