【问题标题】:How to use 'yield' inside async function?如何在异步函数中使用“yield”?
【发布时间】:2016-09-29 17:03:42
【问题描述】:

我想使用生成器 yield 和 async 函数。我读了this topic,然后写了下一段代码:

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' inside async function

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

【问题讨论】:

  • 这可能吗?这似乎是两种相反的设计。除非需要,否则生成器不会产生价值,这意味着它们原则上需要处理具有状态。另一方面,async 表明被调用的函数不能依赖于它的状态。否则你将有数据竞争。支持异步生成器似乎真的很麻烦,它们需要用锁定机制包装。因此,您的问题的答案可能就在这个方向。
  • 您能否返回一个 Future 对象,然后在您需要它的数据时生成该对象。我从来没有使用过 asyncio,但 Tornado 就是这样做的。
  • 我认为异步生成器没有任何意义。您应该能够从异步函数返回生成器。您有什么想要实现的目标,还是只是在尝试?
  • 可以使用事件吗? createGenerator 将被设置事件,并且 start 将是等待事件。我写了this solution。这是工作,但我想要更好的代码。
  • @Ильдар 你看到第二个答案了吗?你怎么想的?看起来有效。

标签: python yield python-3.5 python-asyncio


【解决方案1】:

更新:

从 Python 3.6 开始,我们有 asynchronous generators 并且能够直接在协程中使用 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__ 魔术方法手动实现Asynchronous Iterator。在你的情况下:

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

【讨论】:

【解决方案2】:

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

PEP 0525

What's new in Python 3.6

PS:在编写 Python 3.6 时仍处于测试阶段。如果您使用的是 GNU/Linux 或 OS X,并且迫不及待,您可以使用pyenv 尝试新的 Python。

【讨论】:

    【解决方案3】:

    这应该适用于 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-22
      • 2019-01-06
      • 1970-01-01
      • 2020-03-09
      • 1970-01-01
      • 1970-01-01
      • 2020-01-22
      • 1970-01-01
      相关资源
      最近更新 更多