tqdm progressbar 和 zip 内置不能一起工作

tqdm progressbar and zip built-in do not work together

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 与枚举一起使用,但是必须定义和管理迭代器索引。

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

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

...

Wrapping enumerated iterables: use enumerate(tqdm(...)) instead of tqdm(enumerate(...)). The same applies to numpy.ndenumerate. This is because enumerate functions tend to hide the length of iterables. tqdm does not.

...

Manual control on tqdm() updates by using a with statement:

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

If the optional variable total (or an iterable with len()) is provided, predictive stats are displayed.

with is also optional (you can just assign tqdm() to a variable, but in this case don't forget to del or close() at the end:

pbar = tqdm(total=100)
for i in range(10):
    pbar.update(10)
pbar.close()
如果在 tqdm 调用中提供了 total 关键字参数,

tqdm 可以与 zip 一起使用。

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

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

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

使用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 中注明:

  • Wrapping generators:
    • Generator wrapper functions tend to hide the length of iterables. tqdm does not.
    • Replace tqdm(enumerate(...)) with enumerate(tqdm(...)) or tqdm(enumerate(x), total=len(x), ...). The same applies to numpy.ndenumerate.
    • Replace tqdm(zip(a, b)) with zip(tqdm(a), b) or even zip(tqdm(a), tqdm(b)).
    • The same applies to itertools.
    • Some useful convenience functions can be found under tqdm.contrib.