Python之生成器详解
迭代
维基百科对迭代的定义如下
迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。
可迭代对象(iterable) 与 迭代器(iterator)
关系如下图:
可迭代对象( iterable ) 是实现了__iter__()
方法的对象
迭代器( iterator)是实现了__iter__()
和__next__()
方法的对象
可迭代对象( iterable )通过调用 iter()
方法得到一个 迭代器( iterator)
可迭代对象的特点:能够使⽤for…in…的循环语法的对象,即 list、tuple、str 等类型的对象
迭代器对象的特点:该对象是消耗型的,即对该对象每遍历一次,就消失一个值,遍历完后,就变成了一个空的容器,但不等于 None。
通过如下代码,可以很清楚的认识可迭代对象
与迭代器
的特点与联系
>>> from collections import Iterable, Iterator
>>> a = [1,2,3] # 众所周知,list是一个iterable
>>> b = iter(a) # 通过iter()方法,得到iterator,iter()实际上调用了__iter__(),此后不再多说
>>> isinstance(a, Iterable)
True
>>> isinstance(a, Iterator)
False
>>> isinstance(b, Iterable)
True
>>> isinstance(b, Iterator)
True
# 可见,itertor一定是iterable,但iterable不一定是itertor# iterator是消耗型的,用一次少一次.对iterator进行变量,iterator就空了!
>>> c = list(b)
>>> c
[1, 2, 3]
>>> d = list(b)
>>> d
[]# 空的iterator并不等于None.
>>> if b:
... print(1)
...
1
>>> if b == None:
... print(1)
...# 再来感受一下next()
>>> e = iter(a)
>>> next(e) #next()实际调用了__next__()方法,此后不再多说
1
>>> next(e)
2
然提到了for … in …语句,我们再来简单讲下其工作原理吧,或许能帮助理解以上所讲的内容
>>> x = [1, 2, 3]
>>> for i in x:
... ...
我们对一个iterable用for … in …进行迭代时,实际是先通过调用iter()方法得到一个iterator,假设叫做X.然后循环地调用X的next()方法取得每一次的值,直到iterator为空,返回的StopIteration作为循环结束的标志 for … in … 会自动处理StopIteration异常,从而避免了抛出异常而使程序中断。如图所示
生成器
迭代器与生成器的区别:
1、生成器是一种特殊的迭代器,所以迭代器的用法可以用于生成器
2、要定义一个迭代器,需要分别实现__iter__()
方法和__next__()
方法
3、要定义生成器,只需要一个 yield
生成器,即带有yield的函数,是一种特殊的迭代器,包含生成器函数和生成器表达式(形如(elem for elem in [1, 2, 3])的表达式)
示例代码如下:
>>> a = (elem for elem in [1, 2, 3])
>>> a
<generator object <genexpr> at 0x7f0d23888048>
>>> def fib():
... a, b = 0, 1
... while True:
... yield b
... a, b = b, a + b
...
>>> b = fib()
<generator object fib at 0x7f0d20bbfea0>
生成器的调用方法:
示例代码:
>>> def gen():
... while True:
... s = yield
... print(s)
...
>>> g = gen()
>>> g.send("kissg")
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
>>> next(g)
>>> g.send("kissg")
kissg
问题所在:调用 send(value) 时要注意,要确保 generator 是在yield 处被暂停了,如此才能向 yield 表达式传值,否则将会报错(如上所示),可通过 next() 方法或send(None) 使generator执行到yield。
再来看一段yield更复杂的用法,或许能加深你对generator的next()与send(value)的理解。
>>> def echo():
... for row in range(5):
... s = yield row #若此处 yield 后面无参数,则默认值为 None。通过send(value)方法将value作为yield表达式的当前值
... print("This value is: ", value)
...
>>> g = echo()
>>> g.send(None) # 或者换成 next(g)
0
>>> g.send("hello")
This value is: hello
1
>>>
>>> g.send("world") # 获取 s = yield row 中的 s 值
This value is: world
2
>>>
>>> next(g) # 获取 echo() 对象的值
This value is: None
3
>>>
>>> next(g)
This value is: None
4
使用生成器实现生产者消费者模式,代码如下:
import threading
import time# 消费者
def consumer():data = ''while True:r = yield datadata = '生产的'+r+'还不错'time.sleep(1)# 生产者
def produce(con):con.send(None) #可通过 next() 方法或send(None) 使生成器执行到yield,才能进行下一步生产消费操作for i in range(5):print('生产者生产了%d' % i)r = con.send(str(i))print(r)
if __name__ == '__main__':m = consumer()produce(m)
综上,yield 作为一个暂停恢复的点,代码从yield处恢复,又在下一个yield处暂停。可见,在一次 next() (非首次)或 send(value) 调用过程中,实际上存在2个 yield,一个作为恢复点的 yield 与一个作为暂停点的 yield。因此,也就有2个yield表达式。send(value) 方法是将值传给恢复点yield,调用next()表达式的值时,其恢复点yield的值总是为None,而将暂停点的yield表达式的值返回。
使用yield实现协程
#!/usr/bin/pythondef consumer():r = ''while True:n = yield rif not n:print("not n...")returnprint('[CONSUMER] Consuming %s...' % n)r = '200 OK'def produce(c):f = c.send(None)print('[PRODUCER] Consumer first return: %s' % f)n = 0while n < 2:n = n + 1print('[PRODUCER] Producing %s...' % n)r = c.send(n)print('[PRODUCER] Consumer return: %s' % r)c.close()c = consumer()
produce(c)
运行结果:
[PRODUCER] Consumer first return:
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
协程的说明:
注意到consumer函数是一个generator,把一个consumer传入produce后:
- 首先调用c.send(None)启动生成器;
- 然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
- consumer通过yield拿到消息,处理,又通过yield把结果传回;
- produce拿到consumer处理的结果,继续生产下一条消息;
- produce决定不生产了,通过c.close()关闭consumer,整个过程结束。
整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
参考文档:
http://kissg.me/2016/04/09/python-generator-yield/
https://www.cnblogs.com/fangyuan1004/p/4571304.html
Python之生成器详解相关推荐
- python协程详解
目录 python协程详解 一.什么是协程 二.了解协程的过程 1.yield工作原理 2.预激协程的装饰器 3.终止协程和异常处理 4.让协程返回值 5.yield from的使用 6.yield ...
- python六大数据类型详解
python 六大数据类型详解 文章目录 python 六大数据类型详解 数据类型简介 Number(数值) String(字符串) Python字符串的45个方法详解 一.大小写转换 01.capi ...
- python协程详解_python协程详解
原博文 2019-10-25 10:07 − # python协程详解 ![python协程详解](https://pic2.zhimg.com/50/v2-9f3e2152b616e89fbad86 ...
- python20191031_20191031:Python取反运算详解
20191031:Python取反运算详解 取反运算:~3 == 4 1.对于数字 3 =======>转换为二进制表示为011 2.对011取反为100 3.为什么表示-4 a.计算机用补码表 ...
- Python字符编码详解
Python字符编码详解 转自http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html Python字符编码详解 本文简单介绍了各种常用的 ...
- python的执行过程_在交互式环境中执行Python程序过程详解
前言 相信接触过Python的伙伴们都知道运行Python脚本程序的方式有多种,目前主要的方式有:交互式环境运行.命令行窗口运行.开发工具上运行等,其中在不同的操作平台上还互不相同.今天,小编讲些Py ...
- windows上安装Anaconda和python的教程详解
一提到数字图像处理编程,可能大多数人就会想到matlab,但matlab也有自身的缺点: 1.不开源,价格贵 2.软件容量大.一般3G以上,高版本甚至达5G以上. 3.只能做研究,不易转化成软件. 因 ...
- python变量类型-Python 变量类型详解
变量存储在内存中的值.这就意味着在创建变量时会在内存中开辟一个空间. 基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中. 因此,变量可以指定不同的数据类型,这些变量可以存储整 ...
- python安装教程windows-windows上安装Anaconda和python的教程详解
一提到数字图像处理编程,可能大多数人就会想到matlab,但matlab也有自身的缺点: 1.不开源,价格贵 2.软件容量大.一般3G以上,高版本甚至达5G以上. 3.只能做研究,不易转化成软件. 因 ...
最新文章
- 【校招面试 之 C/C++】第16题 C++ new和delete的实现原理
- 简单天气应用开发——基本功能完成
- springboot actuator_Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控...
- One order event display tool
- Windows CE创建桌面快捷方式
- 硅谷还是程序员的“圣地”吗?
- 每天5分钟玩转docker容器技术 pdf_stack 的优势 每天5分钟玩转 Docker 容器技术(113)...
- 2d头发_3D打印毛囊突破性进展!“头发工厂”将成秃顶的救星
- Java语言中的----条件循环
- SDRAM学习笔记(二)
- 神舟计算机主板bios,神舟笔记本BIOS设置详解
- edge便捷截取长图
- Ubiquitous Religions POJ - 2524
- 一句关于爱情的话...
- python第四次作业——陈灵院
- Office服务器意外响应,Office 所有使用过程中未响应,崩溃,意外关闭
- Mybatis错误 Result Maps collection already contains value for xxx
- win10远程桌面连接都有哪些工具
- 音频基础学习三——声音的时频谱
- 微信小程序富文本渲染(rich-text)换行失效
热门文章
- dnf剑魂buff等级上限_DNF:半年前没人看得上,如今被开发成T0,这神话终于翻身了...
- python画聚类图、并且把聚类图保存起来_Python利用igraph绘制复杂网络聚类(社区检测)结果图-Go语言中文社区...
- 使用“宝塔一键迁移”工具,将单机版typecho博客系统迁移到京东云cvm云主机
- matlab符号对象
- 解决 fatal: unable to access ‘https://github.com/qweertu/git-demo.git/‘: error setting certificate...
- 大学生访谈计算机教师,职业生涯人物访谈报告,教师(共10篇)
- docker重启后启动失败Failed to start Docker Application Container Engine.
- CSS3新增属性汇总
- 号外——安装ubantu操作系统16.04
- 借助github搭建自己的CDN服务