异步生成器不是迭代器?
Async generator is not an iterator?
在 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
我认为,如果您要将某个东西称为迭代器,那是因为它具有完全相同的接口,所以我可以只编写异步迭代器并在严重依赖 next() 调用的框架上使用。
如果您需要重写整个代码才能使用异步,那么任何新的 Python 功能都是毫无意义的。
我是不是漏掉了什么?
谢谢!
我相信为异步生成器引入了一个新声明:
async for TARGET in ITER:
BLOCK
else:
BLOCK2
根据PEP 492。
基本上,这意味着您应该这样做:
async for number in generate(10):
print(number)
此外,检查 Differences from generators:
Native coroutine objects do not implement iter and next
methods. Therefore, they cannot be iterated over or passed to iter() ,
list() , tuple() and other built-ins. They also cannot be used in a
for..in loop. An attempt to use iter or next on a native
coroutine object will result in a TypeError .
我用它来异步循环列表
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
因此,正如@bosnjak 所说,您可以将异步用于:
async for ITEM in A_ITER:
BLOCK1
else: # optional
BLOCK2
但是如果你想手动迭代,你可以简单地写:
it = async_iterator()
await it.__anext__()
但我不建议这样做。
I think that if you are going to call something an Iterator its because it has exactly the same interface, so I can just write async iterators and use on a framework that relies heavily on next() calls
不,实际上不一样。常规同步迭代器和异步迭代器之间存在差异。原因不多:
- Python 协程内部构建在生成器之上
- python之禅,明胜于隐。这样你就会真正看到,代码可以暂停的地方。
这就是为什么无法将 iter
和 next
与异步迭代器一起使用的原因。而且您不能将它们与需要同步迭代器的框架一起使用。所以如果你想让你的代码异步,你也必须使用异步框架。 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 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())
在 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
我认为,如果您要将某个东西称为迭代器,那是因为它具有完全相同的接口,所以我可以只编写异步迭代器并在严重依赖 next() 调用的框架上使用。 如果您需要重写整个代码才能使用异步,那么任何新的 Python 功能都是毫无意义的。
我是不是漏掉了什么?
谢谢!
我相信为异步生成器引入了一个新声明:
async for TARGET in ITER:
BLOCK
else:
BLOCK2
根据PEP 492。
基本上,这意味着您应该这样做:
async for number in generate(10):
print(number)
此外,检查 Differences from generators:
Native coroutine objects do not implement iter and next methods. Therefore, they cannot be iterated over or passed to iter() , list() , tuple() and other built-ins. They also cannot be used in a for..in loop. An attempt to use iter or next on a native coroutine object will result in a TypeError .
我用它来异步循环列表
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
因此,正如@bosnjak 所说,您可以将异步用于:
async for ITEM in A_ITER:
BLOCK1
else: # optional
BLOCK2
但是如果你想手动迭代,你可以简单地写:
it = async_iterator()
await it.__anext__()
但我不建议这样做。
I think that if you are going to call something an Iterator its because it has exactly the same interface, so I can just write async iterators and use on a framework that relies heavily on next() calls
不,实际上不一样。常规同步迭代器和异步迭代器之间存在差异。原因不多:
- Python 协程内部构建在生成器之上
- python之禅,明胜于隐。这样你就会真正看到,代码可以暂停的地方。
这就是为什么无法将 iter
和 next
与异步迭代器一起使用的原因。而且您不能将它们与需要同步迭代器的框架一起使用。所以如果你想让你的代码异步,你也必须使用异步框架。 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 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())