生成器

 1 #生成器
 2 '''
 3 通过列表生成式(列表推导式),可以直接创建一个列表
 4 但是,受到内存限制,列表容量肯定是有限
 5 而且,创建一个包含100万个元素的列表,不仅占用很大内存空间
 6 如果我们仅仅需要访问前面几个元素,那后面大多数元素占用的空间都浪费了
 7 所以,如果列表元素可以按照某种算法推算出来,那我们就可以在循环中不断推算出后续元素
 8 这样就不必创建完整的list,节省大量空间
 9 在python中,这种一边循环,一边计算的机制,成为生成器:generator
10 
11 得到生成器的方式:
12 1 使用列表推导式得到生成器
13 2 借助函数完成
14 
15 '''
16 #1 要得到[0,3,6,9,12,15,18,21,,,27]
17 newlist = [x*3 for x in range(15)]
18 print(newlist)
19 print(type(newlist)) #<class 'list'
20 #得到生成器:把外面的[]换成小括号:
21 g = (x*3 for x in range(5))
22 print(type(g)) #<class 'generator'>
23 
24 #调用生成器:
25 #方式一:用__next__()来调用生成器,每次只生成一个数,只占用当前一个数字的内存
26 print(g.__next__())
27 print(g.__next__())
28 #方式二:next(生成器对象),系统内置函数
29 print(next(g))
30 print(next(g))
31 print(next(g))
32 print(next(g)) #StopIteration - 生成器产生不出更多的数字了,会抛出异常,可以用try except接收这个异常并使用for循环

生成器第二部分:

 1 #生成器
 2 #函数中出现yield关键字,则该函数就是生成器
 3 '''
 4 用函数写生成器的步骤:
 5 1 定义一个函数,函数中使用yield关键字
 6 2 调用函数,接收一下调用的结果
 7 3 得到的结果就是生成器
 8 4 借助于next()或者__next__()来得到元素
 9 
10 '''
11 
12 
13 '''
14 g = (x*3 for x in range(5))
15 while True:
16     try:
17         e = next(g)
18         print(e)
19     except StopIteration as err:
20         print('打印完成')
21         break
22 '''
23 
24 #斐波那切数列
25 '''
26 def func():
27     n = 0
28     while True:
29         n += 1
30         yield n #当函数中出现yield关键字,则该函数就是生成器
31         #yield类似return,yield还表示暂停的意思,下次执行时候从刚才停的位置继续执行
32 g = func()
33 print(g) #<generator object func at 0x0359DC70>
34 print(next(g))
35 print(next(g))
36 print(next(g))
37 print(next(g))
38 '''
39 
40 def fib(length):
41     a,b = 0,1
42     n = 0
43     while n < length:
44         #print(b)
45         yield b #返回b,并在此处暂停
46         a,b = b,a+b
47         n += 1
48     return '没有更多元素了'#什么时候能返回abc:如果生成器没有任何可以生成的内容,则把要StopIteration的报错信息放在return里
49 g = fib(6)
50 print(next(g))
51 print(next(g))
52 print(next(g))
53 print(next(g))
54 print(next(g))
55 print(next(g))
56 print(next(g))
57 print(next(g)) #StopIteration: 没有更多元素了

生成器第三部分:send()

 1 '''
 2 生成器方法:
 3 1 __next__(): 获取下一个元素
 4 2 send(value): 向每次生成器调用中传值,注意:第一次调用send(None),必须往里传None,才可以继续send
 5 
 6 '''
 7 
 8 def gen():
 9     i = 0
10     while i < 5:
11         temp = yield i #temp: None. 把下一次send过来的值赋给temp,可以打印temp的值了,第一次的赋值必须是None
12         print('temp: ',temp)
13         for x in range(int(temp)):
14             print('---> ', x)
15         print('*******************')
16         i += 1
17     return '没有更多的数据了'
18 g = gen()
19 #print(next(g))
20 #print(next(g))
21 #print(next(g))
22 #print(next(g))
23 #print(next(g))
24 #print(next(g))
25 
26 #g.__next__()
27 #g.send(): 生成器里的一个方法
28 '''
29 print(g.send(None)) #第一次赋值必须是None
30 n1 = g.send('haha')
31 print('n1: ',n1) #TypeError: can't send non-None value to a just-started generator
32 '''
33 print(g.send(None))
34 n2 = g.send(3)
35 print('n2: ',n2)
36 n3 = g.send(2)
37 print('n3: ',n3)

生成器应用:

 1 #进程,线程,协程
 2 #一个进程可以包含多个线程,一个线程可以包含多个协程
 3 #协程底层就是用生成器做的
 4 
 5 '''
 6 def task1(n):
 7     for i in range(n):
 8         print('正在搬第{}块砖'.format(i))
 9 
10 def task2(n):
11     for i in range(n):
12         print('正在听第{}首歌'.format(i))
13 
14 #任务交替运行:板一块砖,听一首歌
15 task1(10)
16 task2(5)
17 '''
18 #用生成器来让两个任务切换
19 def task1(n):
20     for i in range(n):
21         print('正在搬第{}块砖'.format(i))
22         yield #yield后面可以不接内容,表示只暂停,不往外扔东西,或只扔空
23 
24 def task2(n):
25     for i in range(n):
26         print('正在听第{}首歌'.format(i))
27         yield None
28 
29 g1 = task1(5)
30 g2 = task2(5)
31 
32 while True:
33     try:
34         next(g1)
35         next(g2)
36     except:
37         print('没事啦')
38         break

 

 

################################################################

https://blog.csdn.net/mieleizhi0522/article/details/82142856

先把yield理解为return

#!/usr/bin/python3.5
# -*- coding=utf-8 -*-

def foo():
	print('starting...')
	while True:
		res = yield 4
		print('res:',res)

g=foo()
print(next(g))
print('*'*20)
print(next(g))

  

root@ubuntu:/home/vigossr/exercise# ./yield_ex.py
starting...
4
********************
res: None
4

我直接解释代码运行顺序,相当于代码单步调试:

1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)

2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环

3.程序遇到yield关键字,然后把yield想想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果,

4.程序执行print("*"*20),输出20个*

5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None,

6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.

到这里你可能就明白yield和return的关系和区别了,带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。

#!/usr/bin/python3.5
# -*- coding=utf-8 -*-

def foo():
	print('starting...')
	while True:
		res = yield 4
		print('res:',res)

g=foo()
print(next(g))
print('*'*20)
#print(next(g))
print(g.send(7))

  root@ubuntu:/home/vigossr/exercise# ./yield_ex.py 

starting...
4
********************
res: 7
4
root@ubuntu:/home/vigossr/exercise#

先大致说一下send函数的概念:此时你应该注意到上面那个的紫色的字,还有上面那个res的值为什么是None,这个变成了7,到底为什么,这是因为,send是发送一个参数给res的,因为上面讲到,return的时候,并没有把4赋值给res,下次执行的时候只好继续执行赋值操作,只好赋值为None了,而如果用send的话,开始执行的时候,先接着上一次(return 4之后)执行,先把7赋值给了res,然后执行next的作用,遇见下一回的yield,return出结果后结束。

 

5.程序执行g.send(7),程序会从yield关键字那一行继续向下运行,send会把7这个值赋值给res变量

6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环

7.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。

 

posted @ 2019-08-19 12:04  月亮上的石头  阅读(117)  评论(0编辑  收藏  举报