闭包

1|0什么是闭包


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

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

2|0分析


假如有个名为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]

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


__EOF__

本文作者cnhkzyy
本文链接https://www.cnblogs.com/my_captain/p/12843822.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   cnhkzyy  阅读(239)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示