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')

  

posted on 2018-03-09 15:51  nicolas_Z  阅读(194)  评论(0)    收藏  举报

导航