闭包

什么是闭包

在一个外函数中定义了一个内函数,内函数里使用了外函数的局部变量,并且外函数的返回值是内函数的引用。一般情况下,如果一个函数结束,函数内部的所有东西都会释放掉,还给内存,局部变量都会消失,但是闭包是一种特殊情况。如果外函数在结束的时候发现有自己的局部变量会在内函数中使用到,就把这个局部变量绑定给了内部函数,然后自己再结束
举个例子:

def outer(a):
    b = 10
    def inner():
        print(a+b)
    return inner


if __name__ == "__main__":
    demo_1 = outer(5)
    print(demo_1)   #15

    demo_2 = outer(7)
    print(demo_2)   #17

分析

假如有个名为avg的函数,它的作用是计算不断增加的系列值的均值,比如:整个历史中某个商品的平均收盘价。每天都会增加新价格,因此平均值要考虑到目前为止所有的价格。使用闭包实现如下:

def make_averager():
    series = []
    
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)
    
    return averager


avg = make_averager()
print(avg(10))
print(avg(11))
print(avg(12))


# 运行结果
10.0
10.5
11.0

调用make_averager时,返回一个averager函数对象,每次调用averager时,它会把参数添加到series中,然后计算平均值。那么avg函数是在哪里寻找的series呢?
注意,series是make_averager函数的局部变量,因为那个函数的定义体中初始化了series:series=[],可以,调用avg(10)时,make_averager函数已经返回了,而它的本地作用域也一去不复返了

在averager函数中,series是自由变量。自由变量是指未在本地作用域中绑定的变量

检查返回的averager函数对象,我们发现Python在__code__属性中保存了局部变量和自由变量的名称

print(avg.__code__.co_varnames)
# 运行结果
('new_value', 'total')


print(avg.__code__.co_freevars)
# 运行结果
('series',)

series的绑定在返回的avg函数的__closure__属性中。avg.__closure__中的各个元素对应于avg.code.co_freevars中的一个名称。这些元素是cell对象,有个cell_contents属性,保存着真正的值

print(avg.__closure__)
# 运行结果
(<cell at 0x0000000000581CD8: list object at 0x00000000001B6288>,)

print(avg.__closure__[0].cell_contents)
# 运行结果
[10, 11, 12]

综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定

posted @ 2020-05-07 16:26  cnhkzyy  阅读(238)  评论(0编辑  收藏  举报