如何在异步函数中使用“yield”?

81

我想使用生成器yield和异步函数。我阅读了这个主题,并编写了以下代码:

import asyncio

async def createGenerator():
    mylist = range(3)
    for i in mylist:
        await asyncio.sleep(1)
        yield i*i

async def start():
    mygenerator = await createGenerator()
    for i in mygenerator:
        print(i)

loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(start())

except KeyboardInterrupt:
    loop.stop()
    pass

但我收到了错误:

SyntaxError: 'yield' 在异步函数中

如何在异步函数中使用生成器的 yield ?


2
这可能吗?它看起来像是两种相反的设计。生成器被设计成只在需要时产生值,这意味着它们原则上需要处理状态。另一方面,async 表示被调用的函数不能依赖于其状态。否则就会出现数据竞争。支持异步生成器似乎非常繁琐,它们需要用锁机制进行包装。所以你的问题的答案可能在这个方向上。 - luk32
你能返回一个 Future 对象,然后在需要它的数据时 yield 这个对象吗?我从未使用过 asyncio,但这是 Tornado 的做法。 - reticentroot
我认为异步生成器没有任何意义。你应该能够从异步函数返回一个生成器。你是想实现什么还是只是在尝试一些东西? - syntonym
可以使用事件吗?createGenerator将设置事件,而start将等待事件。我写了这个解决方案。它能工作,但我想要更好的代码。 - Ильдар
@Ильдар你看见第二个答案了吗?你觉得怎么样?看起来好像可以。 - Mikhail Gerasimov
3个回答

112

更新:

从Python 3.6开始,我们拥有异步生成器并能够在协程中直接使用yield

import asyncio


async def async_generator():
    for i in range(3):
        await asyncio.sleep(1)
        yield i*i


async def main():
    async for i in async_generator():
        print(i)


loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())  # see: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.shutdown_asyncgens
    loop.close()

Python 3.5的旧答案:

你不能在协程中使用yield。唯一的方法是手动实现异步迭代器,使用__aiter__/__anext__魔法方法。在你的情况下:

import asyncio


class async_generator:
    def __init__(self, stop):
        self.i = 0
        self.stop = stop

    async def __aiter__(self):
        return self

    async def __anext__(self):
        i = self.i
        self.i += 1
        if self.i <= self.stop:
            await asyncio.sleep(1)
            return i * i
        else:
            raise StopAsyncIteration


async def main():
    async for i in async_generator(3):
        print(i)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

输出:

0
1
4

以下是另外两个示例:12


1
根据您的Python 3.6代码,我制作了一个使用matlibplot的示例,如果有人感兴趣,请参考:https://dev59.com/QKLia4cB1Zd3GeqPcgV5#44175558 - NumesSanguis

8

新的Python 3.6支持异步生成器。

PEP 0525

Python 3.6有哪些新特性

注意:在撰写本文时,Python 3.6仍处于beta版本。如果您使用的是GNU/Linux或OS X,无法等待可以尝试使用pyenv运行新的Python。


4
这应该适用于Python 3.6(已测试使用3.6.0b1):
import asyncio

async def createGenerator():
    mylist = range(3)
    for i in mylist:
        await asyncio.sleep(1)
        yield i*i

async def start():
    async for i in createGenerator():
        print(i)

loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(start())

except KeyboardInterrupt:
    loop.stop()
    pass

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接