返回介绍

16.4 预激协程的装饰器

发布于 2024-02-05 21:59:47 字数 2242 浏览 0 评论 0 收藏 0

如果不预激,那么协程没什么用。调用 my_coro.send(x) 之前,记住一定要调用 next(my_coro)。为了简化协程的用法,有时会使用一个预激装饰器。示例 16-5 中的 coroutine 装饰器是一例。3

3网上有多个类似的装饰器。这个改自 ActiveState 中的一个诀窍——“Pipeline made of coroutines”,作者是 Chaobin Tang,而他是受到了 David Beazley 的启发。

示例 16-5 coroutil.py:预激协程的装饰器

from functools import wraps

def coroutine(func):
"""装饰器:向前执行到第一个`yield`表达式,预激`func`"""
@wraps(func)
def primer(*args,**kwargs):  ➊
  gen = func(*args,**kwargs)  ➋
  next(gen)  ➌
  return gen  ➍
return primer

❶ 把被装饰的生成器函数替换成这里的 primer 函数;调用 primer 函数时,返回预激后的生成器。

❷ 调用被装饰的函数,获取生成器对象。

❸ 预激生成器。

❹ 返回生成器。

示例 16-6 展示 @coroutine 装饰器的用法。请与示例 16-3 对比。

示例 16-6 coroaverager1.py:使用示例 16-5 中定义的 @coroutine 装饰器定义并测试计算移动平均值的协程

"""
用于计算移动平均值的协程

  >>> coro_avg = averager()  ➊
  >>> from inspect import getgeneratorstate
  >>> getgeneratorstate(coro_avg)  ➋
  'GEN_SUSPENDED'
  >>> coro_avg.send(10)  ➌
  10.0
  >>> coro_avg.send(30)
  20.0
  >>> coro_avg.send(5)
  15.0

"""

from coroutil import coroutine  ➍

@coroutine  ➎
def averager():  ➏
  total = 0.0
  count = 0
  average = None
  while True:
    term = yield average
    total += term
    count += 1
    average = total/count

❶ 调用 averager() 函数创建一个生成器对象,在 coroutine 装饰器的 primer 函数中已经预激了这个生成器。

❷ getgeneratorstate 函数指明,处于 GEN_SUSPENDED 状态,因此这个协程已经准备好,可以接收值了。

❸ 可以立即开始把值发给 coro_avg——这正是 coroutine 装饰器的目的。

❹ 导入 coroutine 装饰器。

❺ 把装饰器应用到 averager 函数上。

❻ 函数的定义体与示例 16-3 完全一样。

很多框架都提供了处理协程的特殊装饰器,不过不是所有装饰器都用于预激协程,有些会提供其他服务,例如勾入事件循环。比如说,异步网络库 Tornado 提供了 tornado.gen 装饰器

使用 yield from 句法(参见 16.7 节)调用协程时,会自动预激,因此与示例 16-5 中的 @coroutine 等装饰器不兼容。Python 3.4 标准库里的 asyncio.coroutine 装饰器(第 18 章介绍)不会预激协程,因此能兼容 yield from 句法。

接下来探讨协程的重要特性——用于终止协程,以及在协程中抛出异常的方法。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文