tqdm进度条和zip内置函数不能一起使用

59

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

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

在迭代执行时,会在控制台打印出一个动态进度条,持续1秒钟: enter image description here

我还没有弄清楚如何与内置的zip对象一起使用tqdm。
这种情况的使用场景是在控制台进度条下迭代两个对应的列表。
例如,我希望以下代码能够正常工作:

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

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

一个解决方法是使用带有enumerate的tqdm,但是这样必须定义和管理迭代器索引。

4个回答

94

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

以下示例演示了如何迭代两个列表中相应元素,并使用工作中的__tqdm__进度条(当使用total关键字参数时): enter image description here

问题在于,tqdm需要事先知道可迭代对象的长度。由于zip被设计用来处理具有不同长度的可迭代对象,因此它没有单个长度属性。

因此,__tqdm__仍然可以很好地与zip一起使用,只需使用total关键字参数提供一点手动控制即可。


1
为了进一步自动化,您可以继续拥有一个列表,如 list = [numbers, letters] 并通过 longest = list[sorted([(i,len(l)) for i,l in enumerate(list)], key=lambda t: t[1])[-1][0]] 获取最大的列表,然后简单地放置 total=len(longest)。这种方法也适用于 itertools.zip_longest - Muneeb Ahmad Khurram

26

使用 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则不会。
    • 请用enumerate(tqdm(...))tqdm(enumerate(x), total=len(x), ...)代替tqdm(enumerate(...))。对于numpy.ndenumerate也是如此。
    • 请用zip(tqdm(a), b)或甚至zip(tqdm(a), tqdm(b))代替tqdm(zip(a, b))
    • itertools同样适用。
    • 一些实用的便捷函数可以在 tqdm.contrib 中找到。

5

如果你有一个进度条,那么你需要能够预测数据结构的长度。

range 实现了 hook 方法 __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
< p > < code > 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()

编辑:

没错,就是这样。看看文档

...

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()

感谢您的这篇文章。它帮助我找到了一种使用“total”关键字参数初始化tqdm并使其与zip配合使用的方法。 - Russell Burdt

-1
from tqdm import tqdm

a = range(10)
b = range(10)

for _ in zip(tqdm(a), b):
    pass

4
当代码附带解释时,它会更加有用。Stack Overflow旨在学习,而不是提供供盲目复制粘贴的片段。特别是在回答已经有答案的老问题时(这个问题已经接近六年了),请编辑您的答案,并解释它如何回答具体的问题,以及如何改进已有答案。请参见[答案]。 - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接