4.3.4 预激协程

在创建生成器时,并不会执行生成器函数中的代码,第一次调用 next(g) 函数时会执行到第一个 yield 处。而协程的 send 函数用来给 yield 左侧的变量赋值。所以在调用 send 函数之前一定要确保已经调用过 next() 函数,这样协程才会停留在 yield 处而不是停留在刚创建的状态。这一步操作叫做协程的预激,上一节的代码中已经演示了这一点。

在使用协程时,一般都需要预激,也就是说 next(g) 其实是一个模板代码,可以被优化掉。要想改变一个函数的运行逻辑,最好的方法是使用装饰器:

import functools

def coroutine(original_coroutine):       
    @functools.wraps(original_coroutine)
    def activate(*args, **kwargs):  # 用 active 函数替换被装饰的 fib 函数
        gen = original_coroutine(*args, **kwargs)  # 调用 fib 函数
        next(gen)  # 预激协程
        return gen   # 像 fib 函数一样,返回预激过的协程
    return activate  # 被装饰的函数 fib 现在替换为了 active 函数,会自动预激

@coroutine
def fib():
    a = 0
    b = 1
    i = 0
    while True:
        i = yield '第{0}个数是: {1}'.format(i, b)
        a, b = b, a + b

fibs = fib()
for i in range(10):
    print(fibs.send(i))

温故一下装饰器的定义,被 coroutine 装饰的 fib 函数等价于:

new_fib = coroutine(fib)
# 根据 coroutine 函数的定义,coroutine(fib) 实际上会返回下面这个函数
def activate():
    gen = fib()
    next(gen)
    return gen

Last updated