在 CPython(即用 C 编写的参考实现,大多数人认为只是“Python”)中,词法闭包被实现为“平面闭包”(请参阅PEP 227 https://www.python.org/dev/peps/pep-0227/)使用单元对象引用,而不是在运行时搜索框架对象(嵌套范围)的链接列表。这允许在返回闭包函数时快速查找并改进垃圾收集。
字节码在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',)
Each 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
功能b
and c
不要直接引用x
,但他们必须为内部功能传播细胞d
来参考它。
上述检查依赖于 CPython 实现细节。在 Python 3.3+ 中你可以调用inspect.getclosurevars https://docs.python.org/3/library/inspect.html#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())