博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python攻克之路-生成器
阅读量:4691 次
发布时间:2019-06-09

本文共 6162 字,大约阅读时间需要 20 分钟。

生成器

通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。如果要创建一个包含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**3   for循环调用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=tIn [11]: aOut[11]: '393'In [12]: bOut[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)
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]: 0In [5]: s.__next__()Out[5]: 1In [6]: next(s)            ##python3上建议使用的内置方法,超出range(10)的范围会报错,因为迭代结束Out[6]: 2In [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)   ...:     456789

  

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)
##是一个生成器对象,只要有yield就是生成器对象In [15]: next(a)okOut[15]: 1 #return 1In [16]: next(a)okOut[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)    ...:     ok1ok2#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 2In [49]: b=boo()In [50]: next(b)ok1Out[50]: 1In [51]: b.send('bbb')   ##没报错ok2Out[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 2In [53]: b=boo()In [54]: b.send(None)     ##相当于next(b)ok1Out[54]: 1                ##已经返回1In [55]: b.send('bbb')    bbbOut[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 21In [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还是变成1In [32]: fibo(6)112358In [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开始011235

补充:before,after=after,before+after运行原理

before=1after=2before,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
#生成器对象,传入的6并不在内存地址,在调用时才会取,而且有限制In [38]: a=fibo(5)In [39]: next(a)Out[39]: 1In [40]: next(a)Out[40]: 1In [41]: next(a)Out[41]: 2

  

 

转载于:https://www.cnblogs.com/reid21/articles/8645697.html

你可能感兴趣的文章
双向链表的实现
查看>>
谈一谈SQL Server中的执行计划缓存(下)
查看>>
vim使用
查看>>
asp.net如何定时执行任务
查看>>
在github上实现页面托管预览功能
查看>>
css选择器
查看>>
prim
查看>>
给陌生人写一封信
查看>>
noip2013花匠
查看>>
[CF]Equalize Them All
查看>>
React Ant design table表单与pagination分页配置
查看>>
重大发现: windows下C++ UI库 UI神器-SOUI(转载)
查看>>
linux 压缩文件的命令总结
查看>>
linux下alias命令具体解释
查看>>
codeforces#254DIV2解题报告
查看>>
自己写的微信小程序炸金花简单版
查看>>
git
查看>>
边工作边刷题:70天一遍leetcode: day 34-1
查看>>
边工作边刷题:70天一遍leetcode: day 45-1
查看>>
Xcode工作区间xxxx.xcworkspace不包含xxxx.xcodeproj
查看>>