我的意思是我从使用async for
中学到什么。这是我用async for
编写的代码,AIter(10)
可以替换为get_range()
。
但是代码的运行方式类似于同步而不是异步。
import asyncio
async def get_range():
for i in range(10):
print(f"start {i}")
await asyncio.sleep(1)
print(f"end {i}")
yield i
class AIter:
def __init__(self, N):
self.i = 0
self.N = N
def __aiter__(self):
return self
async def __anext__(self):
i = self.i
print(f"start {i}")
await asyncio.sleep(1)
print(f"end {i}")
if i >= self.N:
raise StopAsyncIteration
self.i += 1
return i
async def main():
async for p in AIter(10):
print(f"finally {p}")
if __name__ == "__main__":
asyncio.run(main())
我排除的结果应该是:
start 1
start 2
start 3
...
end 1
end 2
...
finally 1
finally 2
...
但是,实际结果是:
start 0
end 0
finally 0
start 1
end 1
finally 1
start 2
end 2
我知道我可以通过使用asyncio.gather
或asyncio.wait
得到例外结果。
但是我很难理解这里使用async for
而不是简单的for
所得到的结果。
如果我想循环几个async for
对象并在一个对象完成后立即使用它们,那么使用Feature
的正确方法是什么?例如:
async for f in feature_objects:
data = await f
with open("file", "w") as fi:
fi.write()
答案 0 :(得分:4)
但是我很难理解这里使用
async for
而不是简单的for
所得到的结果。
潜在的误解是您似乎期望async for
自动对迭代进行并行化。它不会这样做,它只是允许在异步源上进行顺序迭代 。例如,您可以使用async for
遍历TCP流中的行,Websocket中的消息或异步DB驱动程序中的数据库记录。
您不能使用普通的for
来执行上述任何操作,至少不能不阻塞事件循环,因为for
将__next__
作为阻塞函数进行调用,并且不会等待其执行结果。您无法通过手动等待每个元素来进行补偿,因为for
期望__next__
通过引发异常来表示迭代结束-如果__next__
是协程,则该异常在之前不会可见等待它。这就是为什么async for
不仅在Python中被引入,而且在other languages中被引入async / await和广义for
的原因。
如果要并行运行迭代,则需要将它们作为并行协程启动,并使用asyncio.as_completed
或同等功能来检索它们来的结果:
async def x(i):
print(f"start {i}")
await asyncio.sleep(1)
print(f"end {i}")
return i
for f in asyncio.as_completed([x(i) for i in range(10)]):
result = await f
# ... do something with the result ...