Python学习之路(26)——生成器之next和send运行流程
首先还是看一下斐波那契数列:
def fib(num):
count = 0
a,b = 0,1
while count < num:
tmp = a
a = b
b = a + tmp
count +=1
# print(a) #函数
yield a #生成器
print('done.....')
f = fib(100) #生成了一个生成器对象,就是代表推导公式准备好了。有了yield,此时就不是函数了,就成了生成器
函数和生成器仅一步之遥。要把fib函数变成生成器,只需要把print(a)改成yield a就可以了。
生成器和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而生成器,函数在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
对于普通的生成器,第一个next调用,相当于启动生成器,会从生成器函数的第一行代码开始执行,直到第一次执行完yield语句(下例第4行)后,跳出生成器函数。
第二次next调用,进入生成器函数后,从yiedl语句的下一行(第5行)开始执行,然后重新运行到yield语句,执行后跳出生成器函数。
后面第三次调用next,依次类推。
第四次调用next,因为循环只有三次,所有没有yield语句,抛出异常。
def consumer():
r = 'here'
for i in range(3):
yield r
r = '200 OK'+ str(i)
c = consumer()
print(next(c))
print(next(c))
print(next(c))
print(next(c))
执行结果如下:
here
200 OK0
200 OK1
Traceback (most recent call last):
File "D:/Project/Python/Pro_py3/test.py", line 11, in <module>
print(next(c))
StopIteration
了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。
其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,可以认为next(c)和c.send(None)作用是一样的。需要注意的是,第一次调用时,使用next()语句或send(None),不能使用send发送一个非None的值,否则会出错,因为没有Python yield语句来接收这个值。
def consumer():
r = 'here'
for i in range(3):
yield r
r = '200 OK'+ str(i)
c = consumer()
print(c.send('hello'))
Traceback (most recent call last):
File "D:/Project/Python/Pro_py3/test.py", line 8, in <module>
print(c.send('hello'))
TypeError: can't send non-None value to a just-started generator
而对于有yield xxx语句的函数,send()传递的值没有用。
def consumer():
r = 'here'
for i in range(3):
yield r
r = '200 OK '+ str(i)
c = consumer()
print(c.send(None))
print(c.send('hello'))
print(c.send(None))
# 执行结果
here
200 OK 0
200 OK 1
那么send()应该如何使用呢?
我们首先看一下下面这个例子:
def consumer():
r = 'here'
while True:
n1 = yield r
if not n1:
return
print('[CONSUMER] Consuming %s...' % n1)
r = '200 OK ' + str(n1)
def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r1 = c.send(n)
print('[PRODUCER] Consumer return: %s' % r1)
c.close()
c = consumer()
produce(c)
# 执行结果为:
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK 1
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK 2
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK 3
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK 4
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK 5
我们看看send的执行顺序。
当第一次send(None)(对应第11行,相当于next())时,启动生成器,从生成器函数consumer()的第一行代码开始执行,直到第一次执行完yield(对应第4行)后,跳出生成器函数。这个过程中,n1一直没有定义。
下面进行到send(1)时,进入生成器函数,注意这里与调用next不同。从第4行开始,把传递进去的值1赋值给n1,但是并不执行yield部分,然后继续从yield的下一行语句继续执行,然后再次重新运行到yield语句,执行后,跳出生成器函数。
后面以此类推。
send和next相比,只是开始多了一次赋值的动作,其他运行流程是相同的。
下面的例子:
import time
def consumer(name):
print('%s准备吃包子啦!' % name)
while True:
baozi = yield
print('包子[%s]来了,被[%s]吃了'% (baozi, name))
def producer(name):
c1 = consumer("A")
c2 = consumer("B")
next(c1)
next(c2)
print('%s开始准备做包子啦!' % name)
for i in range(10):
time.sleep(1)
print('做了2个包子!')
c1.send(i)
c2.send(i)
producer('nicolas')
浙公网安备 33010602011771号