一.什么是生成器?

生成器可以理解成是一种数据类型,特殊地是生成器可以自动实现迭代器协议
其他的数据类型需要调用自己内置的__iter__方法
所以换种说法,生成器就是可迭代对象

!回忆:很重要的迭代器协议

对象必须提供一个 next 方法,执行该方法要么返回迭代中的下一项,
要么就引起一个Stoplteration异常,以终止迭代(只能往后走不能往前退)

二.生成器的分类(两类)

python中生成器的表现形式
python中提供生成器的方式

一类是生成器函数;另一类是生成器表达式

第一类:关于生成器函数

  • 与常规函数定义相同。但是返回值时使用yield而不是return。
  • yield语句一次返回一个结果,可以进行多次返回(而return只能返回一次)
  • yield每次返回一个结果,在每个结果中间,挂起函数的状态(其实就是记住我函数执行到哪一行了)
#举例:
def test ():
yield 1
g = test() #并不会执行test()函数,需要通过 g.__next__()方法来触发生成器函数执行
print(g)
print(g.__next__()) #执行结果
<generator object test at 0x0051AA70>
1

在说生成器表达式之前,补充三元表达式和列表解析

三元表达式:(顾名思义,就是有三个元素呗)

以前我们是这么写程序的:

name = 'alex'
if name == 'alex':
print('Ok')
else:
print('Not ok')

利用三元表达式我们是这么写程序的:

name = 'alex'
res = 'Ok' if name == 'alex' else 'Not ok' #三元表达式
print(res)
#执行结果
Ok

(每一圈为一个元)

列表解析:

列表解析式的语法格式为:

  • [i操作 for i in 列表 if 表达式1 and 表达式2]
  • (其实就是用中括号[]将三元表达式框起来)

举例理解:
#我要通过程序下10个鸡蛋
#以前我是这么写的

egg_list = []
for i in range(10):
egg_list.append('鸡蛋%s' %i)
print(egg_list)
#执行结果
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']

#通过列表解析式我是这么写程序的

l0 = [ '鸡蛋%s' %i for i in range(10) ]
print(l0)
#执行结果
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9'] l1 = [ '鸡蛋%s' %i for i in range(10) if i < 5 ]
print(l1)
#执行结果
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4'] l2 = [ '鸡蛋%s' %i for i in range(10) if i < 3 or i > 7]
print(l2)
#执行结果
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋8', '鸡蛋9']

总结:列表解析式优缺点
优点:取值方便(如果列表的长度较小时使用列表解析会很方便,)
缺点:如果列表的长度很大的时候,使用列表解析会占用很多的内存资源,此时可以使用生成器表达式来节省内存资源

第二类:关于生成器表达式

生成器表达式:(就是将列表解析式的中括号变成圆括号)

#举例:
l0 = ('鸡蛋%s' %i for i in range(10))
print(l0)
print(l0.__next__())
print(l0.__next__())
print(l0.__next__()) #执行结果
<generator object <genexpr> at 0x0045AA70>
鸡蛋0
鸡蛋1
鸡蛋2

小结:
1.将列表解析式的 [] 换成 () 得到的就是生成器表达式
2.列表解析式与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.python使用迭代器协议让 for 循环变得更加通用。大部分内置函数也是使用迭代器协议来访问对象的

举例:

sum 函数

sum(x ** 2 for x in range(4))  #sum 直接按照迭代器协议访问对象(类比for循环)
sum([x ** 2 for x in range(4)])  #所以并不需要将对象 x ** 2 for x in rang(4) 加上一个中括号变成列表解析式,将所有的值取出来构成一个列表再进行求和运算

三、通过两段程序代码来感受一下生成器的优势

#今天所举得列子不是下蛋就是吃包子(视频课上老师就是这么讲的)
#我也深深的怀疑
#为什么老师这么钟爱吃包子和下鸡蛋

#下蛋程序一:
def xiadan():
res = []
for i in range(10000):
res.append('鸡蛋%s' %i)
return res
print(xiadan()) #缺点一:占用空间较大
#缺点二:效率低
#下蛋程序二:
def xiadan():
for i in range(10000):
yield '鸡蛋%s' %i
lmj = xiadan()
print(lmj.__next__())
#第一段程序是一旦执行 xiadan()这个函数,先下了10000个鸡蛋来占用内存空间,在去执行其他操作
#第二段程序是通过生成器函数yield来返回我所需要的鸡蛋,我边用(通过__next__() 触发生成器函数)鸡蛋,边下鸡蛋

以生成器函数为例,对生成器进行总结

  • 语法上和函数类似:生成器函数和常规函数几乎是一样的。它们都是使用def语句进行定义

差别在于生成器使用yield语句进行返回一个值,而常规函数使用return语句返回一个值

  • 自动实现迭代器协议:对于生成器,python会自动实现迭代器协议,以便应用到迭代器背景中

由于生成器自动实现了迭代器协议,所以我们可以直接调用它的next方法,并且在没有值可以返回的时候生成器自动生成Stoplteration异常

  • 状态挂起:生成器使用yield语句返回一个值。yield 语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行

优点一:
生成器的好处就是延迟计算,一次返回一个结果。也就是说它不会一次生成所有的结果,这对于大数据量
处理非常有用(前面下鸡蛋的例子)

优点二:
生成器还能提高代码可读性

1.使用生成器以后,代码行数更少(在保证代码可读性的前提下,代码行数越少越好)
2.不适用生成器,对于每次结果,我们首先看到的是result.append(index),其次才是index
也就是说,我们每次看到的,是一个列表的append操作,只是append的是我们想要的结果。
使用生成器的时候,直接yield index,少了列表的append操作的干扰,我们一眼能够看出,代码是要进行什么操作。

四、触发生成器执行的三种方式

    • 方式一:__next__()
    • 方式二:next()
    • 方式三:send()
#举例:

def xiadan():
for i in range(10000):
yield '鸡蛋%s' %i g = xiadan()
print(g.__next__())
print(next(g))
print(g.send(None)) #执行结果
鸡蛋0
鸡蛋1
鸡蛋2

关于 send() 总结来源于一下文章并且结合自己的理解
看到一篇关于 yield 总结特别好的文章
链接:https://www.cnblogs.com/renpingsheng/p/8635777.html

send()必须传一个参数,可为 None 或者 其他值

作用:

1. yield相当于return ,控制的是函数的返回值
2. x = yield的另外一个特性,接受send传过来的值,赋值给 x

举例理解:

def test():
print('开始执行函数')
first = yield
print('第一次', first)
second = yield
print('第二次', second)
yield
g = test()
print(next(g))
print(g.send(1))
print(g.send(2))
#程序执行过程分析
# 1.程序开始执行以后,因为test函数中有yield关键字,所以test函数并不会真的执行,而是先得到一个生成器g.
# 2.直到调用next方法,test函数正式开始执行,先执行test函数中的print方法,打印开始执行函数。然后执行first = yield
# 3.程序遇到yield关键字,程序暂停,此时next(g)语句执行完成,打印next(g)执行结果,即yield传回的结果,为None
# 4.程序执行g.send(1),程序会从yield关键字那一行继续向下运行,send会把1这个值传递给yield
# 5.yield接收到send方法传递过来的值,然后由yield赋值给first变量
# 6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次遇到yield关键字,程序暂停,此时g.send(1)语句执行完成,打印g.send(1)执行结果,即yield传回的结果,为None
# 7.程序执行g.send(2),程序会从yield关键字那一行继续向下运行,send会把2这个值传递给yield
# 8.yield接收到send方法传递过来的值,然后由yield赋值给second变量
# 9.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次遇到yield关键字,程序暂停,此时g.send(2)语句执行完成,打印g.send(2)执行结果,即yield传回的结果,为None

写在后面:

珍爱眼睛  远离电子产品

从上了研究生阶段  眼睛就开始有虹膜炎

隔段时间就来打扰我

不能看电脑不能看手机不能看强光

还拼命流眼泪

我也真是佩服自己

正好这个阶段我就在看书学python  也抽时间看了 许三观卖血记

文学素养还是要培养的

我要做祖国新时代的四有新人  有文化 有道德 有。。。还有什么来着

哈哈

爱吃火锅的人运气不会太差

爱吃火锅的人怎么可能轻易放弃

Python小白学习之路(二十二)—【生成器】的更多相关文章

  1. Python小白学习之路(十二)—【前向引用】【风湿理论】

    前向引用 风湿理论(函数即变量) 理论总是很抽象,我个人理解: 代码从上到下执行,一旦遇到定义的函数体,内存便为其开辟空间,并用该函数的名字作为一个标识但是该函数体内具体是什么内容,这个时候并不着急去 ...

  2. Python小白学习之路(十)—【函数】【函数返回值】【函数参数】

    写在前面: 昨天早睡之后,感觉今天已经恢复了百分之八十的样子 又是活力满满的小伙郭 今日份鸡汤: 我始终相信,在这个世界上,一定有另一个自己,在做着我不敢做的事,在过着我想过的生活.-------宫崎 ...

  3. Python小白学习之路(十五)—【map()函数】【filter()函数】【reduce()函数】

    一.map()函数 map()是 Python 内置的高阶函数 有两个参数,第一个是接收一个函数 f(匿名函数或者自定义函数都OK啦):第二个参数是一个 可迭代对象 功能是通过把函数 f 依次作用在 ...

  4. Python小白学习之路(十九)—【文件操作步骤】【文件操作模式】

    一.文件操作步骤 step1:打开文件,得到文件句柄并赋值给一个变量step2:通过句柄对文件进行操作step3:关闭文件 举例: a = open('hello world', 'r', encod ...

  5. Python小白学习之路(十八)—【内置函数三】

    一.对象操作 help() 功能:返回目标对象的帮助信息 举例: print(help(input)) #执行结果 Help on built-in function input in module ...

  6. Python小白学习之路(十四)—【作用域】【匿名函数】【编程方法论】【高阶函数】

    吧啦吧啦内心戏 在没有具体学作用域之前,我在之前的学习笔记中就有提到 我开始以为是自己自创的词儿 没想到这个词早已经存在(手动捂脸) 真是个无知的小火锅(不知者无罪) 我发现自己最擅长做的事情,就是给 ...

  7. Python小白学习之路(十六)—【内置函数一】

    将68个内置函数按照其功能分为了10类,分别是: 数学运算(7个) abs()   divmod()  max()  min()  pow()  round()  sum() 类型转换(24个) bo ...

  8. FastAPI 学习之路(十二)接口几个额外信息和额外数据类型

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  9. Python小白学习之路(二十)—【打开文件的模式二】【文件的其他操作】

    打开文件的模式(二) 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码.图片文件的jgp格 ...

  10. Python小白学习之路(二十六)—【if __name__ =='__main__':】【用状态标识操作】

    规则一: 一个python文件中,只写一些可以运行的功能测试代码写在这句代码下面 if __name__ =='__main__': 在讲这边的时候,我不是很懂参考了一篇博客,地址如下:http:// ...

随机推荐

  1. 2018.10.12 NOIP模拟 棋盘问题(切比雪夫距离)

    传送门 貌似是防ak题? 考试的时候想到了做四次cdqcdqcdq于是给自己多套了一个lognlognlogn结果还MLEMLEMLE 0分.(记得最后5分钟调出来的时候是那么的欣喜 下来发现并不需要 ...

  2. 第四章 代词(Les pronoms )

    ★人称代词 .主语人称代词 第一人称和第二人称属纯人称代词,只能代人不能代物;第三人称可代人,亦可代物.如: La Terre est ronde. Elle tourne autour du Sol ...

  3. Android 长时间运行任务说明

    android 4.0 后,小米手机需要授权 自动启动 (在安全中心权限里设置),不然AlarmManager设置系统闹钟将不起作用

  4. gridcontrol 图片列异步加载

    在gridview中指定一列,将ColumnEdit设置成pictureEdit 在使用showDialog这里窗体后,需要frm.Dispose()将资源释放 1.将该列的UnboundType属性 ...

  5. 使用idea,springboot,springsession,redis实现分布式微服务的session 共享

    本次开发环境:idea2016.3.4 +jdk1.8+maven3.3.9+redis+springboot+jedis 本文中的项目使用Maven来管理项目依赖,使用Spring Session和 ...

  6. day02(继承,重写,this,super,final,抽象类)

    继承 概述:日常生活中儿女总会继承长辈的遗产,java语言也是.通过继承多种实物之间形成一种关系. 特点: ①一个类只能继承一个父类 ②一个父类可以有多个子类 ③可以多层继承(a类继承b类   C类继 ...

  7. (线段树)Balanced Lineup --POJ --3264

    链接: 对于POJ老是爆,我也是醉了, 链接等等再发吧! http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82832#problem/G 只 ...

  8. 几个经典的数学库之一学习---VCGlib(2)

    几个经典的数学库之一学习---VCGlib(2) 1. Optional Component(可选的组件) 有许多Vertex和Face的属性并不是一直都是必要的,如Face-Face的邻接关系.VC ...

  9. hdu 5017 模拟退火/三分求椭圆上离圆心最近的点的距离

    http://acm.hdu.edu.cn/showproblem.php?pid=5017 求椭圆上离圆心最近的点的距离. 模拟退火和三分套三分都能解决 #include <cstdio> ...

  10. 转:getContextPath、getServletPath、getRequestURI的区别

    假定你的web application 名称为news,你在浏览器中输入请求路径: http://localhost:8080/news/main/list.jsp 则执行下面向行代码后打印出如下结果 ...