Python生成器详解 | 投稿
off999 2024-12-10 19:17 16 浏览 0 评论
程派微信号:codingpy
本文为作者、kissg.me博主赵喧典授权编程派原创发布。文章比较长,大家可以收藏了慢慢看哈~~
引文
编程派前几天推送了一篇文章,叫“Python学习进阶路线(简版)”,生成器(generator)赫然在列.可是我不太会.不会怎么办?学咯。于是上网看了不少教程,又看了官方文档,学到了不少知识。在此,权且做个学习笔记,也与大家分享一下。
正文
要理解generator,我们先从迭代(iteration)与迭代器(iterator)讲起.当然,本文的重点是generator,iteration与iterator的知识将点到即止。直接看generator
迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。
以上是维基百科对迭代的定义。在python中,迭代通常是通过for ... in ...来完成的,而且只要是可迭代对象(iterable),都能进行迭代。这里简单讲下iterable与iterator:
iterable是实现了__iter__()方法的对象.更确切的说,是container.__iter__()方法,该方法返回的是的一个iterator对象,因此iterable是你可以从其获得iterator的对象.使用iterable时,将一次性返回所有结果,都存放在内存中,并且这些值都能重复使用.以上说法严重错误!对于iterable,我们该关注的是,它是一个能一次返回一个成员的对象(iterable is an object capable of returning its members one at a time),一些iterable将所有值都存储在内存中,比如list,而另一些并不是这样,比如我们下面将讲到的iterator.
iterator是实现了iterator.__iter__()和iterator.__next__()方法的对象.iterator.__iter__()方法返回的是iterator对象本身.根据官方的说法,正是这个方法,实现了for ... in ...语句.而iterator.__next__()是iterator区别于iterable的关键了,它允许我们显式地获取一个元素.当调用next()方法时,实际上产生了2个操作:
更新
iterator状态,令其指向后一项,以便下一次调用返回当前结果
如果你学过C++,它其实跟指针的概念很像(如果你还学过链表的话,或许能更好地理解)。
正是__next__(),使得iterator能在每次被调用时,返回一个单一的值(有些教程里,称为一边循环,一边计算,我觉得这个说法不是太准确。但如果这样的说法有助于你的理解,我建议你就这样记),从而极大的节省了内存资源。另一点需要格外注意的是,iterator是消耗型的,即每一个值被使用过后,就消失了。因此,你可以将以上的操作2理解成pop。对iterator进行遍历之后,其就变成了一个空的容器了,但不等于None哦。因此,若要重复使用iterator,利用list()方法将其结果保存起来是一个不错的选择。
我们通过代码来感受一下。
既然提到了for ... in ...语句,我们再来简单讲下其工作原理吧,或许能帮助理解以上所讲的内容。
>>> x = [1, 2, 3]
>>> for i in x:
...
...
我们对一个iterable用for ... in ...进行迭代时,实际是先通过调用iter()方法得到一个iterator,假设叫做X。然后循环地调用X的next()方法取得每一次的值,直到iterator为空,返回的StopIteration作为循环结束的标志。for ... in ...会自动处理StopIteration异常,从而避免了抛出异常而使程序中断。如图所示
磨刀不误砍柴工,有了前面的知识,我们再来理解generator与yield将会事半功倍。
首先先理清几个概念:
generator: A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.
generator iterator: An object created by a generator funcion.
generator expression: An expression that returns an iterator.
以上的定义均来自python官方文档。可见,我们常说的生成器,就是带有yield的函数,而generator iterator则是generator function的返回值,即一个generator对象,而形如(elem for elem in [1, 2, 3])的表达式,称为generator expression,实际使用与generator无异。
其实说白了,generator就是iterator的一种,以更优雅的方式实现的iterator。官方的说法是:
Python’s generators provide a convenient way to implement the iterator protocol.
你完全可以像使用iterator一样使用generator,当然除了定义。定义一个iterator,你需要分别实现__iter__()方法和__next__()方法,但generator只需要一个小小的yield(好吧,generator expression的使用比较简单,就不展开讲了。)
前文讲到iterator通过__next__()方法实现了每次调用,返回一个单一值的功能。而yield就是实现generator的__next__()方法的关键!先来看一个最简单的例子:
第一次调用next()方法时,函数似乎执行到yield 1,就暂停了。然后再次调用next()时,函数从yield 1之后开始执行的,并再次暂停。第三次调用next(),从第二次暂停的地方开始执行。第四次,抛出StopIteration异常。
事实上,generator确实在遇到yield之后暂停了,确切点说,是先返回了yield表达式的值,再暂停的。当再次调用next()时,从先前暂停的地方开始执行,直到遇到下一个yield。这与上文介绍的对iterator调用next()方法,执行原理一般无二。
有些教程里说generator保存的是算法,而我觉得用中断服务子程序来描述generator或许能更好理解,这样你就能将yield理解成一个中断服务子程序的断点,没错,是中断服务子程序的断点。我们每次对一个generator对象调用next()时,函数内部代码执行到”断点”yield,然后返回这一部分的结果,并保存上下文环境,”中断”返回。
怎么样,是不是瞬间就明白了yield的用法?
我们再来看另一段代码。
我正是看到这个形式的generator,懵了,才想要深入学习generator与yield的。结合以上的知识,我再告诉你,generator其实有第2种调用方法(恢复执行),即通过send(value)方法将value作为yield表达式的当前值,你可以用该值再对其他变量进行赋值,这一段代码就很好理解了。当我们调用send(value)方法时,generator正由于yield的缘故被暂停了。此时,send(value)方法传入的值作为yield表达式的值,函数中又将该值赋给了变量s,然后print函数打印s,循环再遇到yield,暂停返回。
调用send(value)时要注意,要确保,generator是在yield处被暂停了,如此才能向yield表达式传值,否则将会报错(如上所示),可通过next()方法或send(None)使generator执行到yield。
再来看一段yield更复杂的用法,或许能加深你对generator的next()与send(value)的理解。
上述代码既有yield value的形式,又有value = yield形式,看起来有点复杂。但以yield分离代码进行解读,就不太难了。第一次调用next()方法,执行到yield value表达式,保存上下文环境暂停返回1。第二次调用send(value)方法,从value = yield开始,打印,再次遇到yield value暂停返回。后续的调用se nd(value)或next()都不外如是。
但是,这里就引出了另一个问题,yield作为一个暂停恢复的点,代码从yield处恢复,又在下一个yield处暂停。可见,在一次next()(非首次)或send(value)调用过程中,实际上存在2个yield,一个作为恢复点的yield与一个作为暂停点的yield。因此,也就有2个yield表达式。send(value)方法是将值传给恢复点yield;调用next()表达式的值时,其恢复点yield的值总是为None,而将暂停点的yield表达式的值返回。为方便记忆,你可以将此处的恢复点记作当前的(current),而将暂停点记作下一次的(next),这样就与next()方法匹配起来啦。
generator还实现了另外两个方法throw(type[, value[, traceback]])与close()。前者用于抛出异常,后者用于关闭generator.不过这2个方法似乎很少被直接用到,本文就不再多说了,有兴趣的同学请看这里。
小结
可迭代对象(Iterable)是实现了
__iter__()方法的对象,通过调用iter()方法可以获得一个迭代器(Iterator)。迭代器(Iterator)是实现了
__iter__()和__next__()的对象。for ... in ...的迭代,实际是将可迭代对象转换成迭代器,再重复调用next()方法实现的。生成器(generator)是一个特殊的迭代器,它的实现更简单优雅
yield是生成器实现__next__()方法的关键。它作为生成器执行的暂停恢复点,可以对yield表达式进行赋值,也可以将yield表达式的值返回。
相关推荐
- 手机设置路由器上网(手机如何上网设置路由器)
-
首先,打开手机浏览器,输入路由器的IP地址和密码,进入路由器设置页面。进入后会自动进入【设置向导】,点击设置向导【下一步】。在上网页面,选择【PPPoE(ADSL虚拟拨号)】,点击下一步。点击后,在新...
- office 2016激活工具(office professional 2016 激活工具)
-
word2016激活密钥有两种类型:永久激活码和KMS期限激活密钥。其中,永久激活密钥可以使用批量授权版永久激活密钥进行激活,如所示;而KMS期限激活密钥需要使用KMS客户端密钥进行激活,如所示。另外...
- 苹果zip文件怎么安装(ios安装zip)
-
ZIP文件一般是用于存储多个文件或文件夹的压缩包,因此在安装ZIP文件时需要解压缩文件并提取出其中的内容。具体而言,您可以按照以下步骤来安装ZIP文件:1.下载ZIP文件:从互联网上下载需要安装的Z...
- 如何安装win7双系统(怎么安装win7和win10双系统)
-
1.要安装双系统,首先将第二个系统的分区设置为系统磁盘。在您的电脑上右击,选择管理选项。2.在计算机管理窗口中,选择磁盘管理,找到剩余空间较大的分区,然后右击并选择“压缩卷”。3.输入压缩空间量,其大...
- u盘可以读取但不能写入(u盘只能读取不能写入是什么意思)
-
原因可能有如下几种情况1、U盘不被电脑windows系统所驱动。解决办法:在插上U盘之后,右键点击我的电脑===>属性硬件===>设备管理器==>通用串行总线控制器的位置会有个感叹号...
- 装机系统在哪里下载比较好(装系统去哪里装比较好)
-
对于典型的PC,云下载或本地重新安装都可以正常工作。假设您有一个快速的互联网连接,没有苛刻的下载限制,并且您的PC似乎没有处于损坏状态,那么您应该在合理的时间内让您的PC恢复到新的状态。但是...
- u盘装机制作软件哪个比较好(u盘装机软件推荐)
-
windows微软官方原版系统加上微软的U盘系统制作工具。MediaCreationTool来制作成U盘系统引导盘后来安装系统最纯净安全了。微peu盘驱动好。大白菜老毛桃什么的都用过,都是可以用的,...
- windows7家庭版密钥激活码(win7家庭版激活密钥最新)
-
首先,在桌面上右键点击“计算机”,然后选择“属性”,在窗口底部找到“激活Windows”选项,点击它。然后,选择“在线激活”,输入你的Windows7激活密钥。如果你没有激活密钥,你可以购买一个或者...
- win10好还是win7旗舰版好(windows10好还是windows7旗舰版好)
-
对于个人用户而言,我们更建议购买电脑装Windows10系统。以下是一些原因:兼容性更好:Windows10是微软最新的操作系统,支持大量的最新硬件和驱动程序,高度兼容大多数软件和设备。相比之下,...
-
- 2010office激活密钥25个字符
-
步骤/方式1officeprofessionalplus2010:(office专业版)6QFdx-pYH2G-ppYFd-C7RJM-BBKQ8Bdd3G-xM7FB-Bd2HM-YK63V-VQFdKVYBBJ-TRJpB-QFQ...
-
2025-11-07 01:03 off999
- 刷机(刷机软件)
-
答:用下步骤可把电脑刷机:1、制作启动U盘,如大白菜U盘启动盘,然后将下载的系统iso文件直接复制到U盘的GHO目录下。2、在需要刷机的台式电脑上插入U盘,重启后不停按F12、F11、Esc等快捷键打...
- u盘装linux系统教程(u盘安装linux操作系统安装教程图解)
-
以制作Ubuntulinux系统为例:1、首先要下载Ubuntu系统,在搜索Ubuntu,打开Ubuntu官网。2、然后在下载那里,选择桌面版。3、选择好版本之后,点击下载。4、随后跳转到一个网页,...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
