异步生成器不是迭代器?

use*_*011 14 python python-asyncio

在Python中,您可以编写一个可迭代的生成器,如:

def generate(count):
    for x in range(count):
        yield x

# as an iterator you can apply the function next() to get the values.
it = generate(10)
r0 = next(it)
r1 = next(it) ...
Run Code Online (Sandbox Code Playgroud)

尝试使用异步迭代器时,会出现'yield inside async'错误.建议的解决方案是实现自己的生成器:

class async_generator:
    def __aiter__(self):
        return self
    async def __anext__(self):
        await asyncio.sleep()
        return random.randint(0, 10)

# But when you try to get the next element
it = async_generator(10)
r0 = next(it)
Run Code Online (Sandbox Code Playgroud)

你得到错误"'async_generator'对象不是迭代器"

我认为如果你打算把它称为迭代器,因为它具有完全相同的接口,所以我可以编写异步迭代器并在依赖于next()调用的框架上使用.如果您需要重写整个代码以便能够使用异步,那么任何新的Python功能都是没有意义的.

我错过了什么吗?

谢谢!

小智 10

所以,正如@bosnjak所说,你可以使用async:

async for ITEM in A_ITER:
    BLOCK1
else: # optional
    BLOCK2
Run Code Online (Sandbox Code Playgroud)

但是如果你想手动迭代,你可以简单地写:

it = async_iterator()
await it.__anext__()
Run Code Online (Sandbox Code Playgroud)

但我不建议这样做.

我认为,如果你要调用Iterator,因为它具有完全相同的接口,所以我可以编写异步迭代器并在依赖于next()调用的框架上使用

不,实际上它不一样.常规同步迭代器和异步迭代器之间存在差异.而且原因很少:

  1. Python协程在内部构建在生成器之上
  2. 根据蟒蛇的Zen,显式优于隐式.这样您就可以看到代码可以暂停的位置.

这就是为什么不可能使用iternext使用异步迭代器.并且您不能将它们用于需要同步迭代器的框架.因此,如果要使代码异步,则必须使用异步框架.这里有一些.

另外,我想谈谈迭代器和生成器.迭代器是一个具有__iter____next__方法的特殊对象.而generator是一个包含yield表达式的特殊函数.每个生成器都是迭代器,但反之亦然.异步迭代器和生成器也可以接受同样的事情.是的,因为python 3.6你可以编写异步生成器!

async def ticker(delay, to):
    for i in range(to):
        yield i
        await asyncio.sleep(delay)
Run Code Online (Sandbox Code Playgroud)

您可以阅读PEP 525了解更多详情

  • 您说过每个生成器都是一个迭代器,这适用于异步生成器。那么为什么 Python 会抱怨 `async_generator` 不是迭代器呢? (2认同)
  • 但为什么我可以执行 `''.join([x async for x in xs])` 但不能执行 `''.join(x async for x in xs)` ?这只是 `[]` 具有 async_generators 的特殊语法吗? (2认同)

bos*_*jak 5

我相信为异步生成器引入了新的声明:

async for TARGET in ITER:
    BLOCK
else:
    BLOCK2
Run Code Online (Sandbox Code Playgroud)

根据PEP 492

基本上,这意味着您应该执行以下操作:

async for number in generate(10):
        print(number)
Run Code Online (Sandbox Code Playgroud)

另外,检查与生成器区别

原生协程对象不实现iternext 方法。因此,它们不能被迭代或传递给iter(),list(),tuple()和其他内置函数。它们也不能在for..in循环中使用。尝试在本机协程对象上使用iternext会导致TypeError。


rfe*_*rov 5

在 python 3.10+ 中,您可以使用anext https://docs.python.org/3/library/functions.html#anext

我修改了前面的示例来说明如何anext工作:

import asyncio


async def ticker(to):
    for i in range(to):
        await asyncio.sleep(1)
        yield i


async def main():

    # get only first, failed if nothing yield
    await anext(ticker(3))

    # get only first or default (77)
    await anext(ticker(3), 77)

    # iterate through all
    async for i in ticker(3):
        print(i)

asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)