生成器
根据规则生成你要的东西,在你实际需要的时候生成
1 2
| a = [x*2 for x in range(10)] # [0,2,4,6,8,10,12,14,16,18]
|
有的时候你需要一个很大的列表这样内存就会需要很多,但是不希望现在就生成,而是使用其中某些个
1 2
| # 基本会死掉 a = [x*2 for x in range(100000000000000000)]
|
生成器如何表达
生成器生成方式一
1 2 3 4 5 6 7
| 原来的方式(列表生成式) a = [x*2 for x in range(10)] # [0,2,4,6,8,10,12,14,16,18]
生成器的方式 ==> 将列表生成式的[]改成 () b = (x*2 for x in range(10)) # <generator object <genexpr> at 0x79898312312>
|
如何使用生成器里的值
1 2 3 4 5 6 7 8 9 10 11
| next(b) # 0 next(b) # 2 next(b) # 4 next(b) # 6 next(b) # 8 next(b) # 10 next(b) # 12 next(b) # 14 next(b) # 16 next(b) # 18 next(b) # 异常 如果生成器已经到了最后一个值就罢工了。。。直接异常了
|
生成器生成方式二
- yield 执行到它的时候,creatNum执行就结束了,返回 yield 后的值
如果一个函数里有yield那么 它返回的就是生成器对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| def creatNum(): print('start') a,b = 0,1 for i in range(5) yield b a,b = b,a+b print('stop')
# 创建了一个生成器对象 creatNum()已经不像函数了,而是一个生成器对象 a = creatNum()
# 第一种方式:使用循环执行迭代器 for num in a: print(num)
''' 第二种方式: 两种next方式执行生成器 前提是你要指定生成的次数 res = a.__next__() res = next(a) '''
|
详解 yield
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| def creatNum(): print('start') a,b = 0,1 for i in range(5) yield b a,b = b,a+b print('stop')
a = creatNum() # 生成器对象
next(a) # 此时 a是生成器对象 但是是第一次执行 所以就会执行 ''' 初始化部分 print('start') a,b = 0,1 然后执行循环第一次 返回yield 后的值 b (b = 1) 函数停止在 yield ''' # 第二次调用 next ''' 会从上次停止的地方执行 也就是 yield b (b = 1) 开始 a,b = b,a+b 然后根据循环条件看是否继续循环 发现yield b (b = 1) ''' next(a)
# 第三次同第二次的结果 。。。 # 第四次同第二次的结果 。。。 # 第五次同第二次的结果 。。。 由于循环已经结束 所以最后会打印 stop
|
send
先看如下程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def test(): i = 0 while i<5: temp = yield i print(temp) i += 1
t = test()
a = next(t) # a为0
a = next(t) # 打印 None ,a为1
a = next(t) # 打印 None ,a为2
|
为啥是None
- 如果 yield 前有如下形式 temp = yield i,并不是把 yield值给了temp 而是在下一次执行生成器的时候给它传递一个值
- 之所以为None 因为 yield i 的时候函数就停止了 temp没有执行到
1 2 3 4
| temp = yield i 要看为两部分 左边 temp 和右边 yield i 程序执行的时候会从右往左 但是yield i的时候就结束返回了
|
再看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def test(): i = 0 while i<5: temp = yield i print(temp) i += 1
t = test() a = next(t) # a为0 a = next(t) # 打印 None ,a为1 a = t.send('haha') # 打印 haha ,a 为2
''' t.send('haha') 是把 haha 作为 yield i 的返回结果赋值为temp '''
|
send 和 next的区别
- 相同点:都可以让生成器向下走一步
- 异同点: send可以给一个值,这个值作为 yield i 的整体的结果
使用send的注意点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| def test(): i = 0 while i<5: temp = yield i print(temp) i += 1
t = test()
t.send('haha') # 报错
########################################## 因为第一次从程序开头执行 即i = 0执行 但是 send('haha')是把值给 yield i的整体的返回结果
解决方式就是 第一次先调用 一次 t.next() 然后在 t.send('haha')
第二种方式 if/else
def test(): i = 0 while i<5: if i == 0: temp = yield i else: yield i i += 1
|
生成器的应用
相当于一起执行(就像单核的CPU进程里时间片的概念看上去执行多个应用实际上是假象)
多任务应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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__()
|