【问题标题】:tqdm progressbar and zip built-in do not work togethertqdm 进度条和 zip 内置不能一起工作
【发布时间】:2017-05-01 11:18:02
【问题描述】:

tqdm 是一个 Python 模块,可以在控制台中轻松打印动态更新的进度条。例如

from tqdm import tqdm
from time import sleep
for _ in tqdm(range(10)): 
    sleep(0.1) 

在迭代执行时在控制台中打印一个动态进度条 1 秒:

我还没有弄清楚如何将 tqdm 与内置 zip 对象一起使用。
其用例是使用控制台进度条遍历两个对应的列表。
例如,我希望这可以工作:

for _, _ in tqdm(zip(range(10), range(10))):
    sleep(0.1)

但在这种情况下打印到控制台的进度条不正确:

一种解决方法是将 tqdm 与 enumerate 一起使用,但是必须定义和管理迭代器索引。

【问题讨论】:

    标签: python console progress-bar tqdm


    【解决方案1】:

    如果tqdm 调用中提供了total 关键字参数,则tqdm 可以与zip 一起使用。

    以下示例演示了在使用total 关键字参数的情况下,使用__tqdm__ 进度条对两个列表中的相应元素进行迭代:

    问题是tqdm 需要提前知道可迭代的长度。因为zip 旨在处理具有不同长度的可迭代对象,所以它没有单个长度的参数作为属性。

    所以,__tqdm__ 仍然可以很好地与 zip 配合使用,您只需使用 total 关键字参数提供一点手动控制。

    【讨论】:

      【解决方案2】:

      使用tqdm>=4.42.0,你应该这样做:

      from tqdm.contrib import tzip
      from time import sleep
      
      for _, _ in tzip(range(10), range(10)):
          sleep(0.1)
      

      请注意https://github.com/tqdm/tqdm#faq-and-known-issues:

      • 包装生成器:
        • 生成器包装函数倾向于隐藏可迭代对象的长度。 tqdm 没有。
        • 将tqdm(enumerate(...)) 替换为enumerate(tqdm(...)) 或tqdm(enumerate(x), total=len(x), ...)。这同样适用于numpy.ndenumerate。
        • 将tqdm(zip(a, b)) 替换为zip(tqdm(a), b) 甚至zip(tqdm(a), tqdm(b))。
        • 这同样适用于itertools。
        • 可以在tqdm.contrib 下找到一些有用的便利功能。

      【讨论】:

        【解决方案3】:

        因为你有一个进度条,你可以预测你的数据结构的长度。

        range 实现了hook method __len__,所以你可以发现内置len 的长度

        >>> dir(range(10))
        [ '__le__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index', 'start', 'step', 'stop']
        
        >>> len(range(10))
        10
        

        但是,zip 没有提供猜测包裹结构长度的方法,所以这可能是因为tqdm 无法显示进度条。

        dir(zip(range(10))) # no __len__ here
        ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
        
        >>> len(zip(range(10)))
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: object of type 'zip' has no len()
        

        编辑:

        是的,就是这样。看docs

        ...

        包装枚举迭代:使用enumerate(tqdm(...)) 而不是 tqdm(enumerate(...))。这同样适用于numpy.ndenumerate。这是 因为enumerate 函数倾向于隐藏可迭代的长度。 tqdm 没有。

        ...

        使用with 语句手动控制tqdm() 更新:

        with tqdm(total=100) as pbar:
            for i in range(10):
                pbar.update(10)
        

        如果可选变量 total(或带有 len() 的迭代)是 提供,显示预测统计数据。

        with 也是可选的(您可以将tqdm() 分配给一个变量,但是 在这种情况下,不要忘记在末尾添加del 或close():

        pbar = tqdm(total=100)
        for i in range(10):
            pbar.update(10)
        pbar.close()
        

        【讨论】:

        • 感谢这篇文章。它帮助找到了一种使用 'total' 关键字参数初始化 tqdm 的方法,使其可以与 zip 一起使用。
        猜你喜欢
        • 2017-08-23
        • 1970-01-01
        • 2020-07-17
        • 1970-01-01
        • 2018-10-13
        • 1970-01-01
        • 2022-11-02
        • 2022-12-18
        • 1970-01-01
        相关资源
        最近更新 更多