如何解决`AsyncIterable[T]` 和 `Iterable[Awaitable[T]]` 之间的区别?
取以下两个函数:
import asyncio
def a():
for index in range(2):
# Capture `index` as `local_index` in case awaiting is postponed.
async def next_index(local_index=index):
# Simulate network request.
await asyncio.sleep(0)
return local_index
yield next_index()
async def b():
for index in range(2):
# Simulate network request.
await asyncio.sleep(0)
yield index
a
返回一个 Iterable[Awaitable[int]]
。 b
返回一个 AsyncIterable[int]
。两者的迭代都可以这样完成:
async def main():
for index in a():
print(await index)
async for index in b():
print(index)
asyncio.run(main())
输出:
0
1
0
1
上面例子的关键是我能够在没有外部 Awaitable
的情况下产生 async
,因为内部函数是 async
。
- 就功能而言,
AsyncIterable[T]
是否允许超过Iterable[Awaitable[T]]
?
我也有一个非常相关的问题。来自PEP 492 - Asynchronous Iterators and "async for":
一个异步迭代器对象必须实现一个anext方法 (或者,如果使用 CPython C API 定义,则为 tp_as_async.am_anext 槽) 返回一个等待。
- 因为外部
async
不需要产生Awaitable
,所以__anext__
是否提供比同步__next__
返回Awaitable
的独占功能?立>
这就是我可能遗漏的地方,但根据我目前的理解,异步协议和 StopAsyncIteration
看起来可以使用同步协议和 StopIteration
(当然不那么简洁) ).
解决方法
您遗漏的一个大问题是如何处理循环的结尾。
异步迭代器的 __anext__
返回一个可等待对象,它可以挂起、使用值引发 StopIteration
以生成下一个元素,或者引发 StopAsyncIteration
以表示循环结束。 (PEP 说“要停止迭代 __anext__
必须引发一个 StopAsyncIteration 异常。”,但 StopAsyncIteration 异常确实发生在等待可等待对象时,而不是在调用 __anext__
时同步发生。)
相反,如果您尝试使用可等待元素使常规可迭代,则迭代器的 __next__
需要引发 StopIteration
以结束循环。
这意味着 __next__
不能返回,直到它知道是否会有另一个元素。 __next__
是同步的,所以当它解决这个问题时,控制不能返回到事件循环。这意味着您可能会浪费大量时间同步等待网络流量或其他事情,而所有其他工作都停止了。
您可以通过更多的手动处理来解决这个问题,但它在两端都变得非常尴尬,尤其是迭代器的一端,并且您需要某种等效的 StopAsyncIteration 来消除“这是下一个元素”与“循环已完成”的歧义.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。