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

时间:2017-05-16 16:08:41

标签: python function memory closures

我学习了Python中的闭包,并且我很好地理解了这个概念。在IDLE乱搞时,我想到如果我重新分配封闭函数然后尝试调用封闭函数会发生什么:

SQLiteAssetHelper

我认为这非常有趣,但我意识到我对内存中的闭包发生了什么没有足够的理解等。有人可以解释在重新分配{{f11 :: [[Int]] -> [[Int]] f11 = (filter \x-> length x < 2) . (map (filter \x -> x > 6)) 后如何调用>>> 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 1}?

2 个答案:

答案 0 :(得分:1)

此行为不仅限于闭包。你刚刚做的是创建一个整个函数对象的副本,它引用旧函数,显然它持久,即使你创建了另一个同名函数。

def test(x): return 2*x
t = test
# redeclare the function 
def test(x): return 3*x

测试

>>> test(2)
6
>>> t(2)
4
>>> t is test 
False 

此外,您还可以查看ttest的位置,这些位置显示不同的对象 -

>>> t 
<function test at 0x10f9d6d90>
>>> test 
<function test at 0x10f778f28>

你可以看到它们都是相同的功能,但在不同的位置,因此不同的对象。在你的情况下也会发生同样的事情。

答案 1 :(得分:1)

在CPython中(即用C语言编写的参考实现,大多数人认为只是&#34; Python&#34;),词汇闭包实现为&#39;扁平闭包&#39; (请参阅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())