【问题标题】:Making a tqdm progress bar for asyncio为 asyncio 制作 tqdm 进度条
【发布时间】:2020-07-17 08:29:59
【问题描述】:

我正在尝试收集异步任务的 tqdm 进度条。

希望进度条在任务完成后逐步更新。试了代码:

import asyncio
import tqdm
import random

async def factorial(name, number):
    f = 1
    for i in range(2, number+1):
        await asyncio.sleep(random.random())
        f *= i
    print(f"Task {name}: factorial {number} = {f}")

async def tq(flen):
    for _ in tqdm.tqdm(range(flen)):
        await asyncio.sleep(0.1)

async def main():
    # Schedule the three concurrently

    flist = [factorial("A", 2),
        factorial("B", 3),
        factorial("C", 4)]

    await asyncio.gather(*flist, tq(len(flist)))

asyncio.run(main())

...但这只是完成 tqdm 条然后处理阶乘。

有没有办法让进度条在每个异步任务完成后移动?

【问题讨论】:

    标签: python tqdm


    【解决方案1】:

    截至tqdm version 4.48.0,可以使用tqdm.asyncio.tqdm.as_completed()

    import tqdm.asyncio
    ...
    for f in tqdm.asyncio.tqdm.as_completed(flist):
        await f
    

    【讨论】:

      【解决方案2】:

      现在,我对asyncho 不是特别熟悉,尽管我已经使用tqdm 在python 中的多进程方面取得了一些成功。 您的代码的以下更改似乎会更新进度条并同时打印结果,这可能足以让您开始。

      responses = [await f
                       for f in tqdm.tqdm(asyncio.as_completed(flist), total=len(flist))]
      

      以上内容应替换您的main 定义中的await asyncio.gather(*flist, tq(len(flist)))

      以上内容的灵感来自asyncio aiohttp progress bar with tqdm

      为了只打印一次并更新它,我做了以下操作,更新了进度条的描述以包含您的消息:

      import asyncio
      import tqdm
      
      
      async def factorial(name, number):
          f = 1
          for i in range(2, number + 1):
              await asyncio.sleep(1)
              f *= i
          return f"Task {name}: factorial {number} = {f}"
      
      async def tq(flen):
          for _ in tqdm.tqdm(range(flen)):
              await asyncio.sleep(0.1)
      
      
      async def main():
          # Schedule the three concurrently
      
          flist = [factorial("A", 2),
                   factorial("B", 3),
                   factorial("C", 4)]
      
          pbar = tqdm.tqdm(total=len(flist))
          for f in asyncio.as_completed(flist):
              value = await f
              pbar.set_description(value)
              pbar.update()
      
      if __name__ == '__main__':
          asyncio.run(main())
      

      【讨论】:

      • 这适用于@Dragos,但它在每个阶乘之后分三个步骤打印进度条。有没有办法保持 tqdm 进度条覆盖,而不是三个单独的步骤?
      • @reservoirinvest 我已经用我所做的更新了我的答案,只有一个进度条。这意味着您的打印语句成为进度条上的描述。根据我之前在使用 tqdm 时看到的情况,我认为首先出现 3 个进度条的原因是打印操作
      【解决方案3】:

      对 Dragos 的 pbar 格式的代码进行了一些小改动,并使用 tqdm.write() 几乎得到了我想要的,如下:

      import asyncio
      import random
      
      import tqdm
      
      
      async def factorial(name, number):
          f = 1
          for i in range(2, number + 1):
              await asyncio.sleep(random.random())
              f *= i
          return f"Task {name}: factorial {number} = {f}"
      
      async def tq(flen):
          for _ in tqdm.tqdm(range(flen)):
              await asyncio.sleep(0.1)
      
      
      async def main():
      
          flist = [factorial("A", 2),
                   factorial("B", 3),
                   factorial("C", 4)]
      
          pbar = tqdm.tqdm(total=len(flist), position=0, ncols=90)
          for f in asyncio.as_completed(flist):
              value = await f
              pbar.set_description(desc=value, refresh=True)
              tqdm.tqdm.write(value)
              pbar.update()
      
      if __name__ == '__main__':
          asyncio.run(main())
      

      【讨论】:

        【解决方案4】:

        这是一个围绕 TQDM 的异步包装器,用于返回有序结果:

        import asyncio
        from typing import Any, Coroutine, Iterable, List, Tuple
        
        from tqdm import tqdm
        
        
        async def aprogress(tasks: Iterable[Coroutine], **pbar_kws: Any) -> List[Any]:
            """Runs async tasks with a progress bar and returns an ordered result."""
        
            if not tasks:
                return []
        
            async def tup(idx: int, task: Coroutine) -> Tuple[int, Any]:
                """Returns the index and result of a task."""
                return idx, await task
        
            _tasks = [tup(i, t) for i, t in enumerate(tasks)]
            pbar = tqdm(asyncio.as_completed(_tasks), total=len(_tasks), **pbar_kws)
            res = [await t for t in pbar]
            return [r[1] for r in sorted(res, key=lambda r: r[0])]
        
        
        if __name__ == "__main__":
        
            import random
        
            async def test(idx: int) -> Tuple[int, int]:
                sleep = random.randint(0, 5)
                await asyncio.sleep(sleep)
                return idx, sleep
        
            _tasks = [test(i) for i in range(10)]
            _res = asyncio.run(aprogress(_tasks, desc="pbar test"))
            print(_res)
        

        Full source

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多