从基于生成器的协程转换为本地协程

时间:2019-11-01 11:14:43

标签: python coroutine python-asyncio

我已经使用Python多年了,但是由于我的大部分代码都是用于数据处理的,所以才逐渐研究该语言的一些较晦涩的功能。基于yield的生成器是我的常规工具箱的一部分,最近我读到了有关协程的信息。我发现了一个与此类似的示例:

def averager():
    sum = 0.0
    n = 0
    while True:
        value = yield
        sum += value
        n += 1
        print(sum/n)

avg = averager()
next(avg) # prime the coroutine
avg.send(3)
avg.send(4)
avg.send(5)

打印发送给它的值的平均值。我认为类似的东西可能会在数据处理管道中有用,所以我决定将其放在脑海中。也就是说,直到我在Python documentation中阅读了以下通知:

  

不支持基于生成器的协程,并计划在Python 3.10中删除。

很明显,我想编写面向未来的代码,因此在这一点上开始学习基于生成器的协程可能没有用。那么,我的问题是:如何使用本机(asyncio)协程实现该示例?我要花很多时间来围绕本机协程语法。

在尝试寻找答案时,我发现了一个related question和一个comment和一个answer,它们基本上说“您无法使用{{1} },而是改为使用基于async的协程”。但是,如果这些都消失了,那么有什么办法可以在3.10+中使用协程吗?

1 个答案:

答案 0 :(得分:1)

然后有Asynchronous Generators ...

所以我们在异步上下文中仍然具有那种力量
理论上-提到的 PEP 525 提供了很好的描述,绝对值得一读。
我将发布一个准备好的说明性示例(针对异步averager),其中包括初始化安全终结

import asyncio

async def coro():
    print('running other coroutine in 3 sec ...')
    await asyncio.sleep(3)  # emulate working


async def averager():
    sum_ = n = 0
    while True:
        v = yield
        sum_ += v
        n += 1
        print(sum_ / n)
        await asyncio.sleep(0.1)


async def main():
    agen = averager()
    await agen.asend(None)
    print(agen.__name__, 'initialized ...')

    await agen.asend(3)
    print('another separate processing here ...')

    await coro()

    await agen.asend(4)
    await agen.asend(14)


loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())
    loop.close()

程序输出:

averager initialized ...
3.0
another separate processing here ...
running other coroutine in 3 sec ...
3.5
7.0