Python生成器
1.生成器
通过列表生成式(列表推导式),|我们可以直接创建一个列表。但是,受到内存限制,列表容量中定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们
仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就
不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器: generator
2.通过列表推导式得到生成器
1 newlist = [x * 3 for x in range(10)] 2 print(newlist) 3 4 # 得到生成器 5 g = (x * 3 for x in range(10)) 6 print(type(g)) # >> <class 'generator'> 7 print(g) # >> <generator object <genexpr> at 0x000001FA1AF01548> 8 # 方式一:通过调用__next__()方式得到元素 9 print(g.__next__()) # >> 0 10 print(g.__next__()) # >> 3 11 print(g.__next__()) # >> 6 12 print(g.__next__()) # >> 9 13 14 # 方式二:next() 系统内置的函数:next(生成器对象) 每调用一次则会产生一个元素 15 print(next(g)) # >> 12 16 print(next(g)) # >> 15 17 print(next(g)) # >> 12 18 print(next(g)) # >> 15 19 # StopIteration 生成器本来可以产生10个,得到了10个,再调用next(g),则会抛出异常!
通过循环来调用生成器,并加上产生异常的处理:
1 g = (x * 3 for x in range(10))
2 while True:
3 try:
4 e = next(g)
5 print(e)
6 except:
7 print('元素产生完毕,没有更多的元素了')
8 break
3.借助函数得到生成器
步骤:
1.定义一个函数,函数中使用yield关键字
2.调用函数,接收调用的结构
3.得到的结果就是一个生成器:<generator object func at 0x0000021AEB0B13C8>
4.借助于next(),__next__()得到元素部分
简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,
Python 解释器会将其视为一个generator,
1 # 函数中出现yield关键字,说明函数就不是一个函数,变成了一个生成器
2 def func():
3 n = 0
4 while True:
5 n += 1
6 yield n # --> (return + 暂停)的作用
7
8
9 g = func()
10 print(g) # >> <generator object func at 0x0000021AEB0B13C8>
11 print(next(g)) # >> 1
12 print(g.__next__()) # >>2
13
14 '''
15 yield相当于return+暂定的作用,当n=n+1=1时。返回1,然后暂停在这儿,打印1之后,继续从yield下面开始执行
16 '''
· 斐波那契数列:
1 # 斐波那契数列 2 3 def fib(length): 4 a, b = 0, 1 5 n = 0 6 while n < length: 7 # print(b) 8 yield b # return + 暂停 9 a, b = b, a + b 10 n += 1 11 return '没有更多元素了!!!' # return就是在得到StopIteration,即错误提示信息 12 13 14 g = fib(8) 15 print(next(g)) # >> 1 16 print(next(g)) # >> 1 17 print(next(g)) # >> 2 18 print(next(g)) # >> 3 19 print(next(g)) # >> 5 20 print(next(g)) # >> 8 21 print(next(g)) # >> 13 22 print(next(g)) # >> 21 23 print(next(g)) # >> StopIteration: 没有更多元素了!!!
首先a,b=0,1,0<8,yield b:return b的值,然后暂定,直接跳到print(next(g))
然后跳到下一个print(next(g)),再到a,b=b,a+b这一步,n +=1,说明产生一个元素,
n=1<8,成立,则yield b,即扔出结果1,再又从a,b=b,a+b开始执行。
4.send()函数
生成器方法:
__next__():获取下一个元素
send(value):向每次生成器调用中传值,第一次调用必须传一个空值send(None)
send()的作用是使xx赋值为发送的值(send的参数),然后让生成器执行到下个yield..
使用send(params)需要区分情况。注意:如果生成器未启动,则必须在使用send()前必须要启动生成器,而启动的方法可以是generator.next()或是generator.send(None)
执行到第一个yield处.之后就可以使用send(params)不断传入值了。如果是已启动,则send(params)的作用就是给xx赋值为发送的值(send的参数),然后让生成器执行到下个yield..
为什么需要send(None),也很好理解,因为 生成器还没有走到第一个yield语句,如果我们发生一个真实的值,这时是没有人去接收它的。一旦生成器启动了,就对象接受(即=号左边
的左值xx接受了),之后就可以使用send(params)不断传入值了
1 def gen():
2 i = 0
3 while i < 5:
4 temp = yield i # return 0 ,暂停
5 print('temp:', temp)
6 for x in range(temp):
7 print('----------->',x)
8 print('***************')
9 i += 1
10 return '没有更多数据了'
11
12
13 g = gen()
14 # print(next(g))
15 # print(next(g))
16 # print(next(g))
17
18 print(g.send(None))
19 n1 = g.send(3)
20 print('n1:', n1)
21 n2 = g.send(5)
22 print('n2:', n2)
0
temp: 3
-----------> 0
-----------> 1
-----------> 2
***************
n1: 1
temp: 5
-----------> 0
-----------> 1
-----------> 2
-----------> 3
-----------> 4
***************
n2: 2
5.生成器应用(协程)
1 def task1(n):
2 for i in range(n):
3 print('正在搬第{}块砖'.format(i))
4 yield None
5
6
7 def task2(n):
8 for i in range(n):
9 print('正在听第{}首歌'.format(i))
10 yield None
11
12
13 g1 = task1(5)
14 g2 = task2(5)
15
16 while True:
17 try:
18 next(g1)
19 next(g2)
20 except:
21 break
22
23 '''
24 正在搬第0块砖
25 正在听第0首歌
26 正在搬第1块砖
27 正在听第1首歌
28 正在搬第2块砖
29 正在听第2首歌
30 正在搬第3块砖
31 正在听第3首歌
32 正在搬第4块砖
33 正在听第4首歌
34 '''
作者:Ambitious
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!