如果不预激
,那么协程没什么用
。调用 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
句法。