如果不预激,那么协程没什么用。调用 my_coro.send(x) 之前,记住一定要调用 next(my_ coro)。为了简化协程的用法,有时会使用一个预激装饰器。
示例如下:
from inspect import getgeneratorstate
from functools import wraps
def coroutine(func):
"""装饰器:向前执行到第一个`yield`表达式,预激`func`"""
@wraps(func)
def primer(*args, **kwargs): # 把被装饰的生成器函数替换成这里的 primer 函数;调用 primer 函数时,返回预激后的 生成器
gen = func(*args, **kwargs) # 调用被装饰的函数,获取生成器对象。
next(gen) # 预激生成器。
return gen # 返回生成器。
return primer
@coroutine # 把装饰器应用到 averager 函数上
def averager(): # 实现求平均值
total = 0.0
count = 0
average = None
while True:
term = yield average
total += term
count += 1
average = total / count
if __name__ == '__main__':
# 调用 averager() 函数创建一个生成器对象,在 coroutine 装饰器的 primer 函数中已经 预激了这个生成器。
coro_avg = averager()
# getgeneratorstate 函数指明,处于 GEN_SUSPENDED 状态,因此这个协程已经准备好,可以接收值了
print(getgeneratorstate(coro_avg)) # GEN_SUSPENDED
# 可以立即开始把值发给 coro_avg——这正是 coroutine 装饰器的目的。
print(coro_avg.send(10)) # 10.0
print(coro_avg.send(30)) # 20.0
print(coro_avg.send(5)) # 15.0
这样就不用用next()进行激活协成了
很多框架都提供了处理协程的特殊装饰器,不过不是所有装饰器都用于预激协程,有些会提供其他服务,例如勾入事件循环。比如说,异步网络库 Tornado 提供了 tornado.gen 装饰器(http://tornado.readthedocs.org/en/latest/gen.html)
使用 yield from 句法调用协程时,会自动预激,因此与上面示例中的@coroutine 等装饰器不兼容。Python 3.4 标准库里的 asyncio.coroutine 装饰器不会预激协程,因此能兼容 yield from 句法。
本文探讨了Python中协程的预激机制,通过使用装饰器简化协程的使用,避免了每次都需要调用next()来启动协程的问题。介绍了如何自定义预激装饰器,并展示了在实际代码中的应用。
1266





