48. Python generator 之 next 和 send

Par @Martin dans le
Tags :

对于普通的生成器(参考下面的例子), 第一个 next 调用, 相当于启动生成器, 会从生成器函数的第一行代码开始执行, 直到第一次执行完 yield 语句 (第4行) 后, 跳出生成器函数.
然后第二个 next 调用, 进入生成器函数后, 从 yield 语句的下一句语句 (第5行) 开始执行, 然后重新运行到 yield 语句, 执行后, 跳出生成器函数,后面再次调用 next, 依次类推.

def consumer():
    r = 'here'
    for i in xrange(3):
        yield r
        r = '200 OK' + str(i)

c = consumer()
n1 = c.next()
n2 = c.next()
n3 = c.next()


了解了 next() 如何让包含 yield 的函数执行后, 我们再来看另外一个非常重要的函数 send(msg).

其实 next()send() 在一定意义上作用是相似的, 区别是 send() 可以传递 yield 表达式的值进去, 而 next() 不能传递特定的值, 只能传递 None 进去.

因此, 我们可以看做 c.next()c.send(None) 作用是一样的.

需要提醒的是, 第一次调用时, 请使用 next() 语句或是 send(None),不能使用 send 发送一个非 None 的值, 否则会出错的 (TypeError), 因为没有 Python yield 语句来接收这个值.

下面来着重说明下 send 执行的顺序(参考下面的例子).

当第一次 send(None) (对应11行) 时, 启动生成器, 从生成器函数的第一行代码开始执行, 直到第一次执行完 yield (对应第四行) 后, 跳出生成器函数, 这个过程中, n 一直没有定义.

下面运行到 send(1) 时, 进入生成器函数, 注意这里与调用 next 的不同, 这里是从第四行开始执行, 把 1 赋值给 n, 但是并不执行 yield 部分.

下面继续从 yield 的下一语句继续执行, 然后重新运行到 yield 语句, 执行后, 跳出生成器函数.

sendnext 相比, 只是开始多了一次赋值的动作, 其他运行流程是相同的.

def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        r = '200 OK'

def produce(c):
    c.send(None)
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()

c = consumer()
produce(c)


运行结果:

[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK