Python函数闭包的名称绑定存放在哪里?

Where does Python store the name binding of function closure?

所以最近才明白函数闭包的概念

def outer():
    somevar = []
    assert "somevar" in locals() and not "somevar" in globals()
    def inner():
        assert "somevar" in locals() and not "somevar" in globals()
        somevar.append(5)
        return somevar
    return inner

function = outer()
somevar_returned = function()
assert id(somevar_returned) == id(function.func_closure[0].cell_contents)

据我了解,函数闭包的objective是保持对对象的主动引用,以避免对这个对象进行垃圾回收。这就是以下工作正常的原因:

del outer
somevar_returned_2 = function()
assert id(somevar_returned) == id(function.func_closure[0].cell_contents)
assert id(somevar_returned) == id(somevar_returned_2)

问题是(据我所知)在执行 inner 函数之前,Python 重建局部变量字典。该词典将包含:

问题是 Python 在哪里存储闭包的名称绑定?我到处都找不到。

注意:函数的属性:

>>> print "\n".join("%-16s : %s" % (e, getattr(function, e)) for e in dir(function) if not e.startswith("_") and e != "func_globals")
func_closure     : (<cell at 0x2b919f6bc050: list object at [...]>,)
func_code        : <code object inner at [...], file "<stdin>", line 4>
func_defaults    : None
func_dict        : {}
func_doc         : None
func_name        : inner

幸运的是,Python 中的函数首先是 class 个对象。因此,通过检查函数对象,我们可以对闭包有更多的了解。所有 Python 函数都有一个 closure 属性,让我们检查与闭包函数关联的封闭变量。

>>> def f():
...     pass
...
>>> repr(f); repr(f.__closure__)
'<function f at 0x0153A330>'
'None'

这取决于 python 实现。我假设你的意思是 CPython。

__code__(或func_code)有一个co_freevars属性,其中包含所有非局部变量的名称(它们被称为"free vars",就好像一个python函数是一个逻辑公式,其中参数和局部变量是量化变量)

从这些不同的属性中,您可以获得从本地和非本地名称到单元格的映射。

In [35]: function.__code__.co_freevars
Out[35]: ('somevar',)

co_varnames 属性列出了所有本地定义的名称:

In [36]: function.__code__.co_varnames
Out[36]: ()
In [37]: def outer():
    ...:     somevar = ["Whosebug"]
    ...:     def inner():
    ...:         x = 1
    ...:         somevar.append(5)
    ...:         return somevar
    ...:     return inner
    ...: 
    ...: function = outer()

In [38]: function.__code__.co_varnames
Out[38]: ('x',)

虽然 co_cellvars 表示 inner 函数使用了哪些本地名称:

In [43]: outer.__code__.co_cellvars
Out[43]: ('somevar',)

所有闭包函数都有__closure__属性。此属性 returns 单元格对象的元组。并且单元格对象具有存储变量值的 cell_contents 属性。

In [44]: function.__closure__
Out[44]: (<cell at 0x7f4e06b002b8: list object at 0x7f4e06b522c8>,)
In [45]: function.__closure__[0].cell_contents
Out[45]: ["Whosebug"]