【问题标题】:Async generator is not an iterator?异步生成器不是迭代器?
【发布时间】:2017-02-24 21:32:11
【问题描述】:

在 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) ...

尝试使用异步迭代器时,您会收到“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)

您收到错误"async_generator" object is not an iterator

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

我错过了什么吗?

谢谢!

【问题讨论】:

    标签: python python-asyncio


    【解决方案1】:

    所以,正如@bosnjak 所说,您可以将 async 用于:

    async for ITEM in A_ITER:
        BLOCK1
    else: # optional
        BLOCK2
    

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

    it = async_iterator()
    await it.__anext__()
    

    但我不建议这样做。

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

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

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

    这就是为什么不能将 iternext 与异步迭代器一起使用的原因。而且您不能将它们与需要同步迭代器的框架一起使用。所以如果你要让你的代码异步,你也必须使用异步框架。 Here 很少。

    另外,我想谈谈迭代器和生成器。迭代器是一个特殊的对象,它有__iter____next__ 方法。而生成器是一个包含yield 表达式的特殊函数。 每个生成器都是一个迭代器,反之则不然。异步迭代器和生成器也可以接受同样的事情。是的,从 python 3.6 开始你就可以编写异步生成器了!

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

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

    【讨论】:

    • 你说每个生成器都是一个迭代器,这适用于异步生成器。为什么 Python 会抱怨 async_generator 不是迭代器呢?
    • 但我怎么能做到''.join([x async for x in xs]) 而不能做到''.join(x async for x in xs)?这只是 [] 具有 async_generators 的特殊语法吗?
    • 在 python 3.6 中添加了用于异步理解的新语法 (PEP 530)。所以你可以写[x async for x in xs]。而(x for x in xs) -- 是生成器表达式(在作为函数参数传递时,可以简单地省略另一对括号)。我想你不能用异步代码创建生成器对象。所以(x async for x in xs) 是无效的语法。
    • python 3.10(最终)引入了aiter()anext(),不再需要调用__aiter____anext__docs.python.org/3/library/functions.html#anext
    【解决方案2】:

    我相信为异步生成器引入了一条新语句:

    async for TARGET in ITER:
        BLOCK
    else:
        BLOCK2
    

    根据PEP 492.

    基本上,这意味着你应该这样做:

    async for number in generate(10):
            print(number)
    

    另外,请查看Differences from generators

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

    【讨论】:

    • 那么,我必须假设不存在异步迭代器之类的东西?只是异步发电机?
    • 这就是我所知道的,但我不是专家,尤其是异步。
    【解决方案3】:

    我用它来异步循环列表

    class AsyncRange(object):
        def __init__(self, length):
            self.length = length
            self.i = 0
    
        async def __aiter__(self):
            return self
    
        async def __anext__(self):
            index = self.i
            self.i += 1
            if self.i <= self.length:
                return index
            else:
                raise StopAsyncIteration
    

    那么简单:

    async for i in AsyncRange(my_list):
        # your code
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-13
      • 2016-03-13
      • 2017-11-15
      • 1970-01-01
      • 2019-11-16
      • 1970-01-01
      • 1970-01-01
      • 2014-02-04
      相关资源
      最近更新 更多