6 生成器 yield 协程
1.生成器
----> 1 b = [x*2 for x in range(100000000000)] MemoryError:
想生成一个存放很多数据的列表,但是又不想内存占用太多
每次用一个生成一个,最好
2)列表生成器:生成多个值,每次生成1个
In [29]: c = (x*2 for x in range(10)) In [30]: c Out[30]: <generator object <genexpr> at 0x7f8b5c0c6960> In [31]: next(c) Out[31]: 0 In [32]: next(c) Out[32]: 2 In [40]: next(c) Out[40]: 18 In [41]: next(c) #迭代器没有数据就出现异常了 --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-41-73b012f9653f> in <module>() ----> 1 next(c)
2.斐波那契数列
1)版本1:a,b = b,a+b
def creat_num(): a,b = 0,1 for i in range(5): print(b) a,b = b,a+b creat_num() 1 1 2 3 5
2)版本2:生成器 yield b
def creat_num(): a,b = 0,1 for i in range(5): yield b a,b = b,a+b creat_num()
3)版本3:当成模块导入
可以通过 next() 函数获得生成器的下一个返回值:
def creat_num(): print("--start---") a,b = 0,1 for i in range(5): print("--1--") yield b #每次next执行到yield这里停止 print("--2---") a,b = b,a+b print("--3--") print("--end---")
In [4]: a = creat_num() In [5]: a Out[5]: <generator object creat_num at 0x7facd60b5a98> In [6]: next(a) --start--- --1-- Out[6]: 1 In [7]: next(a) --2--- --3-- --1-- Out[7]: 1 In [8]: next(a) --2--- --3-- --1-- Out[8]: 2
4)版本4:next(a) 和 a.__next__()
def creat_num(): print("--start---") a,b = 0,1 for i in range(5): print("--1--") yield b print("--2---") a,b = b,a+b print("--3--") print("--end---") #创建了1个生成器对象 a = creat_num() ret = next(a) #让a这个生成器对象开始执行,如果第一次执行,就从create_num开始部分执行 #如果是之前已经执行过了,就从上一次停止的位置开始执行 ret = next(a) ret = next(a)
def creat_num(): print("--start---") a,b = 0,1 for i in range(5): print("--1--") yield b print("--2---") a,b = b,a+b print("--3--") print("--end---") #创建了1个生成器对象 a = creat_num() ret = a.__next__() print(ret) #注意: #next(a) #a.__next__() #以上两种方式是一样的
5)版本5:循环取出生成器的值
def creat_num(): print("--start---") a,b = 0,1 for i in range(5): print("--1--") yield b print("--2---") a,b = b,a+b print("--3--") print("--end---") #创建了1个生成器对象 a = creat_num() for i in a: #循环取出值 print(i)
--start--- --1-- 1 --2--- --3-- --1-- 1 --2--- --3-- --1-- 2 --2--- --3-- --1-- 3 --2--- --3-- --1-- 5 --2--- --3-- --end---
3。send(‘haha’)
例子:执行到yield时,gen函数作用暂时保存,返回i的值;temp接收下次c.send("python"),send发送过来的值,c.next()等价c.send(None)
In [10]: def test(): ....: i = 0 ....: while i < 5: ....: temp = yield i ....: print(temp) ....: print(i) ....: i += 1
In [20]: t =test() In [21]: t.__next__() Out[21]: 0 #yield i 的返回值 相当于return i In [22]: t.__next__() --1 None --2 0 Out[22]: 1 In [23]: t.__next__() --1 None --2 1 Out[23]: 2 In [24]: t.__next__() --1 None --2 2 Out[24]: 3 In [25]: t.send('haha') #相当于yield i = haha ,temp =haha --1 haha --2 3 Out[25]: 4 #到yield 停止 yield i 返回return i
2)第一次send(None)
In [26]: t = test() In [27]: t.send('lala') --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-27-e48ba033e48c> in <module>() ----> 1 t.send('lala') TypeError: can't send non-None value to a just-started generator #直接send 出现异常
In [29]: t.send(None) #第一次相当于t.__next__() Out[29]: 0 In [30]: t.send('--lala') --1 --lala --2 0 Out[30]: 1
4.yield 多任务 协程
def test1(): while True: print('===1') yield None def test2(): while True: print('---2') yield None t1 =test1() t2 = test2() while True : t1.__next__() t2.__next__()
### 运行结果 ===1 ---2 ===1 ---2 ===1 ---2 ===1 ---2 ===1 ---2 ===1 ---2
5.总结
生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。
生成器的特点:
- 节约内存
- 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的