簡體   English   中英

異步 python itertools 鏈接多個生成器

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM