Python常见面试题016. 请实现如下功能|谈谈你对闭包的理解
1|0016. 请实现如下功能|谈谈你对闭包的理解
摘自<流畅的python> 第七章 函数装饰器和闭包
-
实现一个函数(可以不是函数)avg,计算不断增加的系列值的平均值,效果如下
-
跟Python常见面试题015.请实现一个如下功能的函数有点类似,但又不太一样
-
关键是你需要有个变量来存储历史值
1|1类的实现方式
-
参考代码
-
avg是个Average的实例
-
avg有个属性series,一开始是个空列表
-
__call__
使得avg对象可以像函数一样调用 -
调用的时候series会保留,因为series只在第一次初始化的时候置为空列表
-
下面的事情就变得简单了
- 但有没有其他做法呢?
- 有的,答案是:闭包
1|2闭包实现
-
参考代码
-
仔细对比2个代码,你会发现相似度是极高的
-
一个是类,一个是函数
-
类中存储历史值的是self.series,函数中的是series
局部变量
-
类实例能调用是实现了
__call__
,函数的实现中,avg是make_average()的返回值averager,是个函数名,所以它也能调用
1|3闭包 closure 初识
-
闭包closure定义:
- 在一个
外函数
中定义了一个内函数
- 内函数里运用了外函数的
临时变量
- 外函数的返回值是
内函数的引用
- 在一个
-
以上面的为例
-
下面这些话你可能听的云里雾里的,姑且听一下。
-
series 是 make_averager 函数的局部变量,因为那个函数的定义体中初始化了series:series = []
-
调用 avg(10) 时,make_averager 函数已经返回了,而它的本地作用域也一去不复返了
-
在 averager 函数中,series 是自由变量(free variable)。这是一个技术术语,指未在本地作用域中绑定的变量
-
averager 的闭包延伸到那个函数的作用域之外,包含自由变量 series 的绑定
1|4反汇编(dis=Disassembler)
-
读懂上面的,不是人干的事情,不过你依然有可能
1|5code属性
-
怎么样不云里雾里呢
-
查看
avg.__code__
属性 -
官方解释
属性 描述 co_argcount 参数数量(不包括仅关键字参数、* 或 ** 参数) co_code 原始编译字节码的字符串 co_cellvars 单元变量名称的元组(通过包含作用域引用) co_consts 字节码中使用的常量元组 co_filename 创建此代码对象的文件的名称 co_firstlineno 第一行在Python源码的行号 co_flags CO_*
标志的位图,详见 此处co_lnotab 编码的行号到字节码索引的映射 co_freevars 自由变量的名字组成的元组(通过函数闭包引用) co_posonlyargcount 仅限位置参数的数量 co_kwonlyargcount 仅限关键字参数的数量(不包括 ** 参数) co_name 定义此代码对象的名称 co_names 局部变量名称的元组 co_nlocals 局部变量的数量 co_stacksize 需要虚拟机堆栈空间 co_varnames 参数名和局部变量的元组 -
通过
__code__
分析 -
结合
avg.__closure__
-
闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。
-
只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量
1|6nolocal 声明
-
前面的make_averager 函数的方法效率不高
-
因为我们把所有值存储在历史数列中,然后在每次调用 averager 时使用 sum 求和
-
更好的实现方式是,只存储目前的总值和元素个数,然后使用这两个数计算均值
-
所以你可能这样实现
-
执行avg(10)的时候你就会报错
-
这个问题你应该看到过,在前面的面试题002中看到过这样的错误
-
关键的错误是在于
-
这样的赋值会把total和length都变成局部变量
-
是对数字、字符串、元组等不可变类型来说,只能读取,不能更新。如果尝试重新绑定,例如 count = count + 1,其实会隐式创建局部变量 count。这样,count 就不是自由变量了,因此不会保存在闭包中
-
为了解决这个问题,Python 3 引入了 nonlocal 声明。它的作用是把变量标记为自由变量,即使在函数中为变量赋予新值了,也会变成自由变量。如果为 nonlocal 声明的变量赋予新值,闭包中保存的绑定会更新。
-
解决的代码
__EOF__

本文链接:https://www.cnblogs.com/wuxianfeng023/p/17303474.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)