可以在没有 await 或 yield 的情况下实现 Python 协程吗?

Posted

技术标签:

【中文标题】可以在没有 await 或 yield 的情况下实现 Python 协程吗?【英文标题】:Can a Python coroutine be implemented without await or yield? 【发布时间】:2019-03-07 08:49:22 【问题描述】:

我正在学习 Python 的 await / async 语法,想知道如何在没有 async、await 或 yield 的情况下实现协程。例如,我使用 async def 语法制作了这个简单的三秒计时器:

import asyncio

async def coroutine():
    count = 0
    while count < 3:
        count += 1
        print(count)
        await asyncio.sleep(1)

loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine())
loop.close()

结果:

1
2
3

我注意到我们可以通过实现 __await__ (https://docs.python.org/3.6/reference/datamodel.html#awaitable-objects) 来实现 Coroutine 对象。这样我就可以成功删除await了。

import asyncio

class Generator():
    def __await__(self):
        count = 0
        while count < 3:
            count += 1
            print(count)
            yield from asyncio.sleep(1)

loop = asyncio.get_event_loop()
loop.run_until_complete(Generator())
loop.close()

最后,我想像这样实现没有yield的迭代器:

import asyncio

class Iterator():

    def __init__(self):
        self.count = 0

    def __iter__(self): return self

    def __await__(self): return self

    def __next__(self):
        if self.count < 3:
            self.count += 1
            print(self.count)
            return next(asyncio.sleep(1))
        else:
            raise StopIteration()

loop = asyncio.get_event_loop()
result = loop.run_until_complete(Iterator())
loop.close()

但它没有用。它在显示“1”后停止。

我知道这没有任何实际价值,但我想知道它以正确理解 asyncio。我可以在没有 await 或 yield 的情况下实现协程吗?如果有怎么办?我用 Python 3.6.7 测试过。

【问题讨论】:

关于yield,我认为这是必需的,因为它允许解释器进行上下文切换。 可能是的,但我很想知道手写迭代器与 yield 作为协程相比究竟缺少什么。如果我在 __next__() 中返回 None 而不是 asyncio.sleep(1),则循环不会抱怨它并按我的预期工作。 【参考方案1】:

虽然协同程序是使用生成器实现的,但协同程序不是生成器:

import asyncio
from typing import Generator


def gen():
    yield 1


async def coro():
    pass


print(isinstance(gen(), Generator))   # True
print(isinstance(coro(), Generator))  # False

这是有意的,因为生成器只不过是协程实现的一个细节。迭代器也是如此:协程不是迭代器。

loop.run_until_complete 期望收到诸如asyncio 协程或未来之类的等待。您正在尝试传递迭代器 - 具有不同行为的不同对象。


长话短说,如果你想实现与asyncio兼容的协程:

async def定义函数 或实现__await__魔术方法

请阅读asyncio doc 了解示例。

如果您想了解协程如何在非常低的级别上工作的一般概念,您可以关注excellent video,其中 David Beazley 从头开始​​实现协程和事件循环。

【讨论】:

以上是关于可以在没有 await 或 yield 的情况下实现 Python 协程吗?的主要内容,如果未能解决你的问题,请参考以下文章

什么是EcmaScript语法中的[Yield,Await,In,Return]

理解Python协程:从yield/send到yield from再到async/await

Java await wait sleep yield

理解Python协程:从yield/send到yield from再到async/await

理解Python协程:从yield/send到yield from再到async/await

await Task.Yield(); 超简单理解!