Python攻克之路-生成器
生成器
通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。如果要创建一个包含100万个元素的列表,不仅占用很大的存储空间,或者仅仅需要访问前面几个元素,加载入内存的其他元素就充分浪费内存空间.
所以,如果列表元素可以按照某种算法推算出来,那是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的内存空间。在Python中,这种一边循环一边计算的机制(通俗说就是需要一个值取一个值,而且只会占用一个元素的内存空间),称为生成器(generator)
1. 列表生成式****
描述:生成一个列表的表达式,一般列表[1,2,3],如下是把for x in range(10)内容作遍历放到前面的x
注意:第一个元素和for后的元素要保持一致,如前面是x后面也要是x
In [1]: [x for x in range(10)]
Out[1]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [3]: [x*2 for x in range(10)] ##可以对列表做操作,生成新的列表
Out[3]: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
生成式中存放函数
先定义一个函数
In [5]: def f(n):...: return n**3for循环调用
In [6]: [f(x) for x in range(10)]
Out[6]: [0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
In [7]: a=[f(x) for x in range(10)]
In [8]: type(a)
Out[8]: list
两个元素赋值(注:赋值的元素数量要两边相等)
In [9]: t=('393',8)
In [10]: a,b=t
In [11]: a
Out[11]: '393'
In [12]: b
Out[12]: 8
2. 生成器****
描述:通过如下方式只能取得一个对象,如果是列表表达式,有10个值,相当于是10盘已经作好的菜,什么时候要吃就吃,列表也是可以这样操作,但是存在一个问题它占用内存空间,生成器就相当于它是一个厨师,什么时候要吃一道菜时,可以让它做出来,它只需要调用一个方法,不吃就不会占用空间,所以当列表数量特别大时,列表相当占用内存空间
生成器的两种创建方式
创建方式a. s=(x for x in range(10))
In [13]: s=(f(x) for x in range(10)) #修改封闭方式,把中括号修改成小括号
In [14]: print(s)
<generator object <genexpr> at 0x7f7ddef4b0a0> ##生成器对象In [15]: [f(x) for x in range(100000000000000000000)]
Killed
使用生成器的next方法来调用值(next方法只能按顺序来取值)
In [3]: s=(x for x in range(10))
In [4]: s.__next__() ##在python2上调用没有下划线,但是在python3是内部特殊方法的调用,不建议这样使用
Out[4]: 0
In [5]: s.__next__()
Out[5]: 1
In [6]: next(s) ##python3上建议使用的内置方法,超出range(10)的范围会报错,因为迭代结束
Out[6]: 2
In [7]: next(s)
Out[7]: 3
生成器本向是一个可迭代对象,所以可以使用for循环
描述:a.s是一个数,遍历时会把第一个数取出,实际是for对s在内部进行一个next的调用来取值,第一次把next的值赋值给i,就可以打印出来了0,再循环再回来取得1,然后之前的0就会作为垃圾被回收,即使打印100万个数也只是占用一个数的内存空间,因为没有被引用的会被python的解释器所回收
b.for反复的调用next,但是按照常理,到最后一个数时代表迭代结束,应该会报错误StopIteration,这时for循环通过异常检测出来,也就是通过except的关键字来捕错误,一旦发生错误except就可以取得,来判断是否迭代结束,然后就直接返回不再做任何的处理
In [8]: for i in s:...: print(i)...:
4
5
6
7
8
9
b. yield创建方式
一般函数
In [4]: def f():...: return 1...: f()...:
Out[4]: 1
yield:(主要用于协程)
执行顺序
In [12]: def f():...: print('ok')...: yield 1 ##类型于return的功能,把1返回,如果下面再有代码不会执行,除非再加yield...: print('ok')...: yield 2...: ##在yield 2后面有一个默认的return none,代表函数结束
In [13]: a=f() ##f()是一个生成器对象,赋值给a,这个结束后不会执行f,不会执行fIn [14]: print(a)
<generator object f at 0x7f013e847a40> ##是一个生成器对象,只要有yield就是生成器对象In [15]: next(a)
ok
Out[15]: 1 #return 1In [16]: next(a)
ok
Out[16]: 2 #return 2
for循环调用原理
描述:调用f()生成器对象,调用next,第一次调用next时,print('ok'),然后返回1,再把1赋值给i,再next,从yield1开始走,print('ok'),返回2,再赋值给i,print(2),第三次再进去函数发现没有yield在会
发生迭代错误,for捕捉异常,迭代结束
注:for i in 后面加的是可迭代对象,for i in后面可以加一个列表[1,2,3],但是列表不是迭代器,因为假设a=[1,2,3],a.时就没有next方法,可迭代对象是内部有__iter__()方法的才是,列表、元组、字典
都有inter方法
In [17]: for i in f():...: print(i)...:
ok
1
ok
2
#for内部实际运行过程
In [18]: for i in f():...: while True:...: i=next(f())
c.生成器的第二个方法send
描述:send与next功能相似,而且还可以向函数传入值,也就是可以给yield前面的变量传入值
In [48]: def boo():...: print('ok1')...: yield 1...: print('ok2')...: yield 2
In [49]: b=boo()
In [50]: next(b)
ok1
Out[50]: 1
In [51]: b.send('bbb') ##没报错
ok2
Out[51]: 2
第一次send时只能使用none,因为它不知道赋值给那个变量,如果第一次send前有next,即使不知道传入给那个变量也不会报错
流程:b.send(None)与next(b)一样,进入函数体,打印ok1,到count=yield1,这里yield 1是直接返回1,第2次b.send('bbb'),这里传入bbb值会赋值给count,再执行打印count,yield 2直接返回2
作用:有时需要与程序交互,需要在调用它时给它一些参数,再用send传入给它一个指导作用
In [52]: def boo():...: print('ok1')...: count=yield 1...: print(count)...: yield 2
In [53]: b=boo()
In [54]: b.send(None) ##相当于next(b)
ok1
Out[54]: 1 ##已经返回1
In [55]: b.send('bbb')
bbb
Out[55]: 2
3.通过生成器yield实现伪并发
描述:很久前CPU只有颗,在同一时刻,CPU只能执行一个任务,但是却做到"伪并"发的效果,如在电脑上既看电影,同时又在QQ聊天,实际是在两个程序之间不段的切换来执行,只是在切换过程中速度快到人所无法感知,如下不太贴切吃苹果就是模拟这种伪并发,一个产苹果,A,B同时吃的简单例子
In [5]: import timeIn [6]: def consumer(name): ##吃苹果的人...: print("%s Prepare apple!" %name) ##传入参数A是人名,准备苹果,接着是B...: while True:...: apple = yield ##yield状态停住,下次有参数传入,apple可以接收...: print("apple[%s]is coming,[%s]eat!"%(apple,name))...: In [7]: def producer(name): ##生产苹果的人,一产一吃达到这种并发的效果...: c = consumer('A') ##创建两个变量,执行consumer函数分别传入参数A,B...: c2 = consumer('B') ##这两行生成一个生成器对象...: c.__next__() ##通过next执行,返回,A准备吃苹果...: c2.__next__() ##通过next执行,返回,B准备吃苹果...: print("I am ready to eat!")...: for i in range(2):...: time.sleep(1)...: print("prepare two apples!") ##就可以准备,模拟停止了1秒钟,准备两个苹果...: c.send(i) ##发送i给c生成器对象,c带i进入到consumer函数apple=yield中,i就给apple这个变量,for i in range(2),从0开始,apple=0,就打印apple[0]is coming,[A]eat!,然后while循环继续又回到yield,c.send(i)就执行完成,再到c2.send(i)执行...: c2.send(i)In [8]: producer("reid")
A Prepare apple!
B Prepare apple!
I am ready to eat!
prepare two apples!
apple[0]is coming,[A]eat!
apple[0]is coming,[B]eat!
prepare two apples!
apple[1]is coming,[A]eat!
apple[1]is coming,[B]eat!
4.斐波那契数列
# 0 1 1 2 3 5 8 13 21
In [18]: def fibo(max):...: n,before,after=0,0,1 ##n是第几位,before前一位数,after是后一位数...: while n < max: ...: print(after) ##从1开始打印...: before,after=after,before+after
#第2位1的值已经打印出来,要做相应的计算,一开始before是0,after是1,移位后before是1,after是before+after=0+1...: n = n + 1 ##让n+1进行下一次打印,第二次打印after,从1还是变成1
In [32]: fibo(6)
1
1
2
3
5
8In [33]: def fibo(max):...: n, before, after=0, 0, 1...: while n < max:...: print(before)...: before,after=after,before+after...: n = n + 1...: In [34]: fibo(6) ##打印before从0开始
0
1
1
2
3
5
补充:before,after=after,before+after运行原理
before=1
after=2
before,after=after,before+after
描述:先从右边开始运行before,after=(after=2),(before+after=1+2=3),再给左边赋值
使用yield来返回,使用next来调用斐波那契数列
In [35]: def fibo(max):...: n,before,after=0,1,1...: while n<max:...: yield before #yield有断层,第一次执行完后,把这个状态保存了,下次再从这里开始...: before,after=after,before+after...:
In [37]: fibo(6)
Out[37]: <generator object fibo at 0x7f013d4a7990> #生成器对象,传入的6并不在内存地址,在调用时才会取,而且有限制
In [38]: a=fibo(5)
In [39]: next(a)
Out[39]: 1
In [40]: next(a)
Out[40]: 1
In [41]: next(a)
Out[41]: 2
转载于:https://www.cnblogs.com/reid21/articles/8645697.html
Python攻克之路-生成器相关推荐
- Python攻克之路-xml模块
xml模块 描述:xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但是Json使用起来更简单,json还没有诞生时,xml已经开始使用很久,至今很多传统公司如金融行业很多系统的接口 ...
- Python攻克之路-random模块
random模块 描述:生成随机数 random常用方法 random In [2]: random.random() #0-1之间 Out[2]: 0.2295625620781645 randin ...
- Python攻克之路-网络编程(文件上传实现思路)
需求:一个server,一个client,实现client把某个文件传到server中某个目录中 分析:实际是实现数据传输,设定一个命令和一个参数(上传的内容),连接后,让用户输入命令和要传送的内容, ...
- Python攻克之路-高阶函数
高阶函数 在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数: 接受一个或多个函数作为输入 输出一个函数 在数学中它们也叫做算子(运算符)或泛函.微积分中的导数就是常见的例子,因为它映射一个函 ...
- Python攻克之路-hashlib模块
hashlib模块 描述:加密模块,从明文加密成密文,主要是md5和sha md5 In [13]: import hashlib In [14]: m=hashlib.md5() In [15]: ...
- Python 进阶之路 (九) 再立Flag, 社区最全的itertools深度解析(上)
前言 大家好,今天想和大家分享一下我的itertools学习体验及心得,itertools是一个Python的自带库,内含多种非常实用的方法,我简单学习了一下,发现可以大大提升工作效率,在sf社区内没 ...
- Python文档字符串生成器:基于CodeBERT,支持Google、Numpy等多种输出格式
木易 发自 凹非寺 量子位 报道 | 公众号 QbitAI 又一款懒人神器问世了: Visual Studio Code的扩展,基于CodeBERT的Python文档字符串生成器. 看来现在,这群偷 ...
- Python之迭代器和生成器(Day17)
一.可迭代对象(iterable) 刚才说过,很多容器都是可迭代对象,此外还有更多的对象同样也是可迭代对象,比如处于打开状态的files,sockets等等.但凡是可以返回一个迭代器的对象都可称之为可 ...
- python之路 mysql 博客园_教为学:Python学习之路(二):MySQLdb的几种安装方式,以及用Python测试连接MySql...
教为学:Python学习之路(二):MySQLdb的几种安装方式,以及用Python测试连接MySql Easy_install安装MySQLdb 很简单,以至于我不晓得该怎么说.一句话. sodu ...
- python有关迭代器和生成器的面试题_【面试题 | Python中迭代器和生成器的区别?】- 环球网校...
[摘要]今天给大家解答一道Python常见的面试题,希望这个面试栏目,给那些准备面试的同学,提供一点点帮助!小编会从最基础的面试题开始,每天一题.如果参考答案不够好,或者有错误的话,麻烦大家可以在留言 ...
最新文章
- 人工智能改变未来教育的5大方式
- js脚本 处理js注入
- 西南交大计算机专硕就业怎么样,国内四所交通大学,有985也有211,就业、深造容易,值得报考...
- JSP,JSF和EL简介
- hwclock: Open of /dev/rtc failed, errno=19: No such device.
- torch.nn.parameter.Parameter分析
- windows下php命令行模式错误信息
- java切割文件出现1k_java实现把一个大文件切割成N个固定大小的文件
- php 保存json格式数组 json_encode /u 不转义
- 小程序学习笔记(4)-猫眼电影案例
- Office Tool Plus v8.2.4.0 安装Office组件小工具
- html修改鼠标手势,css要怎么设置鼠标手势?
- 无线系列-WiFi信号波形产生器
- php长微博,用Word一键发布长微博
- 公司新加了一台友宝自动售货机引发的思考-适配器模式
- LeetCode笔记05:最长公共前缀
- MSP430F5529LP(一)IIC与OLED的HELLOWRLD
- Python数据结构11:树的实现,树的应用,前中后序遍历,二叉查找树BST,平衡二叉树AVL树,哈夫曼树和哈夫曼编码
- ​PDF虚拟打印机(pdfFactory) v5.12 官方版
- 刘大拿python_零基础Python知识点回顾(一)
热门文章
- 注意sizeof()返回的数无符号数,有符号数遇到无符号数时变成无符号数
- 如何在 Mac 上使用“接力”回到上次离开的地方?
- MAMP Pro for Mac(PHP/MySQL开发环境)
- 18 Strings for Mac(Xcode文件翻译工具)
- SmartSVN 14 for Mac(多平台SVN客户端)
- DVD-Cloner 2021 for mac(DVD光盘刻录工具)
- 如何在Mac上将您的Apple ID更改为其他电子邮件地址?
- 通信原理实践(五)——2PSK 与2DPSK 通信系统
- win7下声音图标消失的解决办法
- windows 下 MyEclipse 运行hadoop 出错