[英]asynchronous python itertools chain multiple generators
更新的清晰度問題:
假設我有 2 個處理生成器函數:
def gen1(): # just for examples,
yield 1 # yields actually carry
yield 2 # different computation weight
yield 3 # in my case
def gen2():
yield 4
yield 5
yield 6
我可以用 itertools 鏈接它們
from itertools import chain
mix = chain(gen1(), gen2())
然后我可以用它創建另一個生成器函數對象,
def mix_yield():
for item in mix:
yield item
或者只是如果我只想next(mix)
,它就在那里。
我的問題是,如何在異步代碼中執行等效操作?
因為我需要它:
next
迭代器上一篇更新:
經過試驗和研究,我發現aiostream庫聲明為 itertools 的異步版本,所以我做了什么:
import asyncio
from aiostream import stream
async def gen1():
await asyncio.sleep(0)
yield 1
await asyncio.sleep(0)
yield 2
await asyncio.sleep(0)
yield 3
async def gen2():
await asyncio.sleep(0)
yield 4
await asyncio.sleep(0)
yield 5
await asyncio.sleep(0)
yield 6
a_mix = stream.combine.merge(gen1(),gen2())
async def a_mix_yield():
for item in a_mix:
yield item
但我還是不能做next(a_mix)
TypeError: 'merge' object is not an iterator
或next(await a_mix)
raise StreamEmpty()
雖然我仍然可以把它做成一個列表:
print(await stream.list(a_mix))
# [1, 2, 4, 3, 5, 6]
所以一個目標完成了,還有一個目標:
以收益方式返回(一個接一個),或使用next
迭代器
- 最快的解決產量優先(異步)
Python 的next
內置函數只是在對象上調用底層__next__
方法的一種便捷方式。 異步等效的__next__
是__anext__
上異步迭代方法。 沒有下anext
全局函數,但可以很容易地編寫它:
async def anext(aiterator):
return await aiterator.__anext__()
但是節省的空間很小,在極少數情況下需__anext__
時,還可以直接調用__anext__
。 異步迭代器又通過調用__aiter__
(類似於常規迭代器提供的__iter__
)從異步迭代器中獲得。 手動驅動的異步迭代如下所示:
a_iterator = obj.__aiter__() # regular method
elem1 = await a_iterator.__anext__() # async method
elem2 = await a_iterator.__anext__() # async method
...
當沒有更多元素可用時, __anext__
將引發StopAsyncIteration
。 要循環異步迭代器,應該使用async for
。
這是一個基於您的代碼的可運行示例,同時使用__anext__
和async for
來耗盡使用aiostream.stream.combine.merge
設置的流:
async def main():
a_mix = stream.combine.merge(gen1(), gen2())
async with a_mix.stream() as streamer:
mix_iter = streamer.__aiter__()
print(await mix_iter.__anext__())
print(await mix_iter.__anext__())
print('remaining:')
async for x in mix_iter:
print(x)
asyncio.get_event_loop().run_until_complete(main())
我遇到了這個答案,並查看了 aiostream 庫。 這是我想出的用於合並多個異步生成器的代碼。 它不使用任何庫。
async def merge_generators(gens:Set[AsyncGenerator[Any, None]]) -> AsyncGenerator[Any, None]:
pending = gens.copy()
pending_tasks = { asyncio.ensure_future(g.__anext__()): g for g in pending }
while len(pending_tasks) > 0:
done, _ = await asyncio.wait(pending_tasks.keys(), return_when="FIRST_COMPLETED")
for d in done:
try:
result = d.result()
yield result
dg = pending_tasks[d]
pending_tasks[asyncio.ensure_future(dg.__anext__())] = dg
except StopAsyncIteration as sai:
print("Exception in getting result", sai)
finally:
del pending_tasks[d]
希望這對您有所幫助,如果有任何錯誤,請告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.