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

Fun*_*ayu 7 python closures cpython python-2.x

所以最近我理解了函数闭包的概念.

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)
Run Code Online (Sandbox Code Playgroud)

根据我的理解,函数闭包的目的是保持对对象的活动引用,以避免垃圾收集此对象.这就是为什么以下工作正常:

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)
Run Code Online (Sandbox Code Playgroud)

在执行inner函数之前,事情是(总是和我理解的一样),Python重建了locals变量字典.这本词典将包含:

  • 函数的闭包名称与其单元格内容相关联
  • 函数的参数名称与其默认值或给定的参数相关联(并且它可以覆盖先前的名称)

问题是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
Run Code Online (Sandbox Code Playgroud)

Bak*_*riu 8

这取决于python实现.我猜你的意思是CPython.

__code__(或func_code)有一个co_freevars包含所有非本地变量的名称(他们被称为"免费增值经销商",仿佛蟒蛇功能是这里的参数和局部变量进行量化变量的逻辑公式)属性

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

In [35]: function.__code__.co_freevars
Out[35]: ('somevar',)
Run Code Online (Sandbox Code Playgroud)

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

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

In [38]: function.__code__.co_varnames
Out[38]: ('x',)
Run Code Online (Sandbox Code Playgroud)

虽然co_cellvars内部函数使用了哪些本地名称:

In [43]: outer.__code__.co_cellvars
Out[43]: ('somevar',)
Run Code Online (Sandbox Code Playgroud)

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

In [44]: function.__closure__
Out[44]: (<cell at 0x7f4e06b002b8: list object at 0x7f4e06b522c8>,)
In [45]: function.__closure__[0].cell_contents
Out[45]: ["stackoverflow"]
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常好的答案。您能否验证/解释如何从参数和闭包名称重建本地字典? (2认同)
  • @FunkySayu 我正在调查它。看起来 `locals()` 会调用 [`PyEval_GetLocals`](https://hg.python.org/cpython/file/tip/Python/ceval.c#l4433) ,它会调用 [`PyFrame_FastToLocalsWithError`](https: //hg.python.org/cpython/file/tip/Objects/frameobject.c#l867)。我不明白的是它引用了 python 中似乎无法提供的“co_localsplus”属性。无论如何,你可以看到它使用 `co_varnames`、`co_freevars` 等来构建 `locals` 字典。 (2认同)