Python常见面试题016. 请实现如下功能|谈谈你对闭包的理解
016. 请实现如下功能|谈谈你对闭包的理解
摘自<流畅的python> 第七章 函数装饰器和闭包
实现一个函数(可以不是函数)avg,计算不断增加的系列值的平均值,效果如下
def avg(...):
pass
avg(10) =>返回10
avg(20) =>返回10+20的平均值15
avg(30) =>返回10+20+30的平均值20
跟Python常见面试题015.请实现一个如下功能的函数有点类似,但又不太一样
关键是你需要有个变量来存储历史值
类的实现方式
参考代码
class Average():
def __init__(self):
self.series = []
def __call__(self, value):
self.series.append(value)
return sum(self.series)/len(self.series) avg = Average()
print(avg(10))
print(avg(20))
print(avg(30))
avg是个Average的实例
avg有个属性series,一开始是个空列表
__call__使得avg对象可以像函数一样调用调用的时候series会保留,因为series只在第一次初始化的时候置为空列表
下面的事情就变得简单了
- 但有没有其他做法呢?
- 有的,答案是:闭包
闭包实现
参考代码
def make_average():
series = []
def averager(value):
series.append(value)
return sum(series)/len(series)
return averager
avg = make_average()
print(avg(10))
print(avg(20))
print(avg(30))
仔细对比2个代码,你会发现相似度是极高的
一个是类,一个是函数
类中存储历史值的是self.series,函数中的是series
局部变量类实例能调用是实现了
__call__,函数的实现中,avg是make_average()的返回值averager,是个函数名,所以它也能调用
闭包 closure 初识
闭包closure定义:
- 在一个
外函数中定义了一个内函数 - 内函数里运用了外函数的
临时变量 - 外函数的返回值是
内函数的引用
- 在一个
以上面的为例
def make_average(): # 外函数
series = [] # 临时变量(局部变量)
def averager(value): # 内函数
series.append(value)
return sum(series)/len(series)
return averager # 返回内函数的引用
下面这些话你可能听的云里雾里的,姑且听一下。
series 是 make_averager 函数的局部变量,因为那个函数的定义体中初始化了series:series = []
调用 avg(10) 时,make_averager 函数已经返回了,而它的本地作用域也一去不复返了
在 averager 函数中,series 是自由变量(free variable)。这是一个技术术语,指未在本地作用域中绑定的变量

averager 的闭包延伸到那个函数的作用域之外,包含自由变量 series 的绑定
反汇编(dis=Disassembler)
from dis import dis
dis(make_average)
2 0 BUILD_LIST 0
2 STORE_DEREF 0 (series)
3 4 LOAD_CLOSURE 0 (series)
6 BUILD_TUPLE 1
8 LOAD_CONST 1 (<code object averager at 0x000002225DD1CBE0, file "<ipython-input-1-a43a8601eedd>", line 3>)
10 LOAD_CONST 2 ('make_average.<locals>.averager')
12 MAKE_FUNCTION 8 (closure)
14 STORE_FAST 0 (averager)
6 16 LOAD_FAST 0 (averager)
18 RETURN_VALUE
Disassembly of <code object averager at 0x000002225DD1CBE0, file "<ipython-input-1-a43a8601eedd>", line 3>:
4 0 LOAD_DEREF 0 (series)
2 LOAD_METHOD 0 (append)
4 LOAD_FAST 0 (value)
6 CALL_METHOD 1
8 POP_TOP
5 10 LOAD_GLOBAL 1 (sum)
12 LOAD_DEREF 0 (series)
14 CALL_FUNCTION 1
16 LOAD_GLOBAL 2 (len)
18 LOAD_DEREF 0 (series)
20 CALL_FUNCTION 1
22 BINARY_TRUE_DIVIDE
24 RETURN_VALUE
读懂上面的,不是人干的事情,不过你依然有可能
https://docs.python.org/zh-cn/3/library/dis.html#bytecodes
code属性
怎么样不云里雾里呢
查看
avg.__code__属性[_ for _ in dir(avg.__code__) if _[:2]=='co']
['co_argcount',
'co_cellvars',
'co_code',
'co_consts',
'co_filename',
'co_firstlineno',
'co_flags',
'co_freevars',
'co_kwonlyargcount',
'co_lnotab',
'co_name',
'co_names',
'co_nlocals',
'co_posonlyargcount',
'co_stacksize',
'co_varnames']
官方解释
属性 描述 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__分析def make_average():
series = []
def averager(value):
series.append(value)
total = sum(series)
return total/len(series)
return averager
avg = make_average()
avg.__code__.co_varnames # 参数名和局部变量的元组
# ('value', 'total') # value是参数,total是局部变量名
avg.__code__.co_freevars
# ('series',) # 自由变量的名字组成的元组(通过函数闭包引用)结合
avg.__closure__avg.__closure__
# (<cell at 0x000002225FA4DC70: list object at 0x000002225EE35600>,)
# 这是个cell对象,list对象
len(avg.__closure__) # 1
avg.__closure__[0].cell_contents # [] 因为你还没调用
avg(10)
avg(20)
avg(30)
avg.__closure__[0].cell_contents # [10, 20, 30] 保存着真正的值闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。
只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量
Python常见面试题016. 请实现如下功能|谈谈你对闭包的理解的更多相关文章
- 整理的最全 python常见面试题(基本必考)
整理的最全 python常见面试题(基本必考) python 2018-05-17 作者 大蛇王 1.大数据的文件读取 ① 利用生成器generator ②迭代器进行迭代遍历:for line in ...
- 整理的最全 python常见面试题
整理的最全 python常见面试题(基本必考)① ②③④⑤⑥⑦⑧⑨⑩ 1.大数据的文件读取: ① 利用生成器generator: ②迭代器进行迭代遍历:for line in file; 2.迭代 ...
- 315道Python常见面试题
第一部分,Python基础篇 为什么学习Python? 通过什么途径学习的Python? Python和Java.PHP.C.C#.C++等其他语言的对比? 简述解释型和编译型编程语言? Python ...
- Python常见面试题
Q 1:Python 有哪些特点和优点? 作为一门编程入门语言,Python 主要有以下特点和优点: ● 可解释● 具有动态特性● 面向对象● 简明简单● 开源● 具有强大的社区支持当然,实际上 Py ...
- python常见面试题(三)
问题1 到底什么是Python?你可以在回答中与其他技术进行对比(也鼓励这样做). 答案 下面是一些关键点: Python是一种解释型语言.这就是说,与C语言和C的衍生语言不同,Python代码在运行 ...
- python常见面试题(mark)
1.大数据的文件读取 ① 利用生成器generator ②迭代器进行迭代遍历:for line in file 2.迭代器和生成器的区别 1)迭代器是一个更抽象的概念,任何对象,如果它的类有next方 ...
- python常见面试题(二)
1. 到底什么是Python?你可以在回答中与其他技术进行对比(也鼓励这样做). 下面是一些关键点: Python是一种解释型语言.这就是说,与C语言和C的衍生语言不同,Python代码在运行之前不需 ...
- python常见面试题(一)
1.Python是如何进行内存管理的? 答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制 一.对象的引用计数机制 Python内部使用引用计数,来保持追踪内存中的对象,所有对象都 ...
- 【python常见面试题】之python 中对list去重的多种方法
在python相关职位的面试过程中,会对列表list的去重进行考察.(注意有时会要求保证去重的顺序性) 1.直观方法 li=[1,2,3,4,5,1,2,3] new_li=[] for i in l ...
- 10道Python常见面试题
1.MySQL索引种类 1.普通索引 2.唯一索引 3.主键索引 4.组合索引 5.全文索引 2.索引在什么情况下遵循最左前缀的规则? 最左前缀原理的一部分,索引index1:(a,b,c),只会走a ...
随机推荐
- Day3 准备步入入门.ok
安装开发环境(6.21周一) 卸载JDK 删除Java的安装目录 打开我的电脑-->属性-->高级系统设置-->环境变量 删除JAVA_HOME 删除path下添加的JAVA目录 安 ...
- python 循环 类型转换
- 用C#语言实现记事本
一.实验内容: 二.记事本所需功能: (1)记事本程序具有文件的新建.打开.保存功能: (2)文字的复制.粘贴.删除功能:字体类型.格式的设置功能: (3)查看日期时间等功能,并且用户可三根据需要显示 ...
- wrangler 13 mwz 脱浆记录
100度水泡了1个小时,一点没缩 70度水泡了3个小时,一点没缩 随后绝望,放洗衣机里洗了20min,缩了2cm 想再接再厉,放洗衣机30min,没缩 放外面晾干,看晾干以后多长吧 --------- ...
- DHCP分配IP的流程
1.DHCP客户端以广播的形式发送DHCP Discover报文 2.所有的DHCP服务端都可以接收到这个DHCP Discover报文,所有的DHCP服务端都会给出响应,向DCHP客户端发送一个DH ...
- 图的基本操作 (c语言)
图的基本操作:创建 删除 遍历 创建:邻接矩阵和邻接表 十字链表 下面代码是邻接矩阵的定义和邻接表的创建 遍历:深度优先遍历(一条路走到黑) 广度优先遍历(树的层次遍历) 具体代码: #include ...
- python补全用法,windows环境和linux环境
一.windows中python tab具体如下: 1.python3环境装好后,初始环境是没有装readline模块的,先装它. pip install pyreadline 2.在在python的 ...
- the third change day
2022.5.9 今日名言:青春是一个短暂的美梦,当你醒来的时候,它早已消失的无影无踪.----莎士比亚 早起听了一堂听力课,感觉他教的挺好,准备跟着试试,快考试了,别来不及了. 目录 听力技巧 阅读 ...
- TFlite 多线程并行
import numpy as np from multiprocessing import Process, Queue def process_data(data, model_TSNet, ts ...
- jquery input标签的禁用和启用disabled
input框使用disabled后 input 元素既不可用,也不可点击.可以设置 disabled 属性,直到满足某些其他的条件为止(比如选择了一个复选框等等).然后,就需要通过 JavaScrip ...