如何显式地“刷新”tqdm进度条?

24

我经常看到,tqdm进度条会被其他的打印语句破坏,例如:

 93%|█████████▎| 28/30 [00:02<00:00, 13.44it/s]Subject S9
100%|██████████| 30/30 [00:02<00:00, 12.94it/s]
 93%|█████████▎| 28/30 [00:02<00:00, 11.49it/s]Pickling...
100%|██████████| 30/30 [00:02<00:00, 11.47it/s]

这里只应该显示2个进度条。然而,当某些文本打印中断进度条时,剩余部分会在之后打印出来。

有没有办法“刷新”进度条呢?

我读到了tqdm默认将打印输出到stderr,并尝试将其刷新。

sys.stderr.flush()

但这并没有起到帮助作用。

所有上述情况都发生在 PyCharm 控制台模拟器中,因此可能与此有关。


5
在PyCharm之外进行测试。从我的经验来看,PyCharm提供的控制台有时会出现一些问题。 - Ignacio Vergara Kausel
10个回答

21
默认情况下,tqdm会打印到stderr。而你的语句Subject...Pickling...则会打印到stdout。默认情况下,这两个流是不同步的。
如果你希望tqdmprint保持同步,可以将tqdm路由到stdout而不是stderr。可以通过以下方式实现:
tqdm(range(50), file=sys.stdout)

你就不需要刷新标准输出(stdout)了。

5

要在指定时间刷新,请使用refresh()。 如果tqdm卡住了,您应该调用tqdm.close(self)以在结束时显式刷新。 示例:

import time
i_range=tqdm(range(5))
for i in i_range:
        i_range.refresh()
        time.sleep(1)
i_range.close()    

更复杂的嵌套循环示例:

progress = tqdm(range(5*3 ) )
for i in range(5):
    print '============================='
    for j in range(3):
        time.sleep(1)
        progress.update()
        progress.refresh()
progress.close()

请注意,tqdm有与刷新频率相关的参数

mininterval:浮点数,可选。最小进度显示更新间隔 [默认值:0.1] 秒。maxinterval:浮点数,可选。最大进度显示更新间隔 [默认值:10] 秒。


请问您能解释一下第二个例子吗?您创建了一个tqdm对象,但是您没有对其进行迭代——这里会发生什么? - HelloGoodbye
1
我使用.update()而不是迭代。这类似于在循环中显式调用其.next()方法,而不是对可迭代对象进行迭代...每次调用progress.update()时,它都会将tqdm对象向前推进一个... tqdm对象的范围是第一个循环大小乘以第二个循环大小...我添加了refresh()命令,以便在屏幕上显示更新后的值。 - ntg
1
范围是(5*3),因为外部循环重复了5次,内部循环重复了3次。 - ntg
啊哈,看起来非常有用! - HelloGoodbye
@Filip:很抱歉我拒绝了你的更改,但是从快速浏览来看,它们创建了一个(可能更好,但是相当不同的)答案。我建议您将代码发布为新答案,因为我认为它可能会有很多贡献(而且这也会使您获得声望,而不是我;) ) - ntg

5
我认为你最好的选择(因为tqdm会接管输出)是使用
tqdm.write 所以如果你有一个进度条,你可以像这样尝试打印:
In [19]:     from tqdm import tqdm
    ...:     import time
    ...:
    ...:     for i in tqdm(xrange(50)):
    ...:         if i & 0x1 == 0:
    ...:           tqdm.write(str(i))
    ...:           time.sleep(0.5)
    ...:
0
2
4
6
8
10
12
 14%|███████████▌                                                                       | 7/50 [01:50<11:16, 15.73s/it]---------------------------------------------------------------------------             | 11/50 [00:03<00:10,  3.62it/s]

这将把内容打印出来并单独保留底部的进度条。

如果想要显式地刷新进度条,您可以尝试使用tqdm.refresh

In [16]: gen = tqdm(xrange(50))
  0%|                                                                                           | 0/50 [00:00<?, ?it/s]
In [17]: for i in gen:
    ...:     if i & 0x1 == 0:
    ...:       print str(i)
    ...:       gen.refresh()
    ...:       time.sleep(0.5)
    ...:
    ...:
    ...:
    ...:
0
  2%|█▋                                                                                 | 1/50 [00:01<01:17,  1.59s/it]2
  6%|████▉                                                                              | 3/50 [00:02<00:55,  1.19s/it]4
 10%|████████▎                                                                          | 5/50 [00:02<00:40,  1.10it/s]6
 14%|███████████▌                                                                       | 7/50 [00:03<00:30,  1.41it/s]8
 14%|███████████▌                                                                       | 7/50 [00:03<

但是,正如您所看到的,在不使用tqdm.write的情况下,您仍然会打印出条形图旁边的字符。


0
回答如何刷新tqdm进度条的问题(当你不想重复出现进度条或者遇到一些错误时):
from tqdm import tqdm
# YOUR CODE
tqdm._instances.clear()

在Jupyter Notebook中,可以通过以下方法解决问题: {{link1:在Jupyter Notebook中使用tqdm会重复打印新的进度条}}

0
解决方案是强制输出:tqdm.write 等待一个字符串,并允许指定结束符。
for sentences_db, itdqm in zip(sentences_dbs, tqdm(range(len(sentences_dbs)))):
    tqdm.write(itdqm.__str__(), end='')

你可以使用文件属性(例如:file=sys.stderr)来强制输出,因为当速度非常快时,输出会出现方向问题(stdout而不是stderr)。导入sys模块即可实现。


0
这对我有用:
print(f'First print')
for x in tqdm(some_list):
    some_operation = 1+1

# Surround next print with some sleepy time to have give the previous tqdm bar time to finish
time.sleep(0.5)
print(f'Second print')
time.sleep(0.5)

for y in tqdm(some_other_list):
    some_other_operation = 1+3+3+7

0

只是尝试用我可以的方式帮助。

import sys
from tqdm import tqdm
from time import sleep
print('This is done')
sleep(.5)
for i in tqdm(range(0,30), total = (30), desc = 'Subject S9'):
    sleep(.3)
    sys.stdout.flush()
sleep(.5)

for i in tqdm(range(0,30), total = (30), desc = 'Pickling...'):
    sleep(.3)
    sys.stdout.flush()

sleep(.5)

输出将会是:

This is done
Subject S9: 100%|██████████| 30/30 [00:09<00:00,  3.30it/s]
Pickling...: 100%|██████████| 30/30 [00:09<00:00,  3.30it/s]

在每次迭代之间加入sleep()将有助于给它足够的时间来结束进程,然后才进行下一次迭代。 希望这会有所帮助。如有不对之处请指正 :)


0
在这个上下文中,"flush" 的意思是将缓冲区的内容打印到控制台。由于输出已经在控制台上,这并不能解决你的问题。
tqdm 假设光标自上次写入以来没有移动,因此如果有输出到控制台,它将在光标所在的位置输出更新。即使你通过 tqdm.write() 重定向所有其他输出,它只会清除带有进度条的行,写入输出并重新绘制进度条。这非常低效,并且在输出过多时会出现问题。
我建议使用其他进度条库,比如 Enlighten,它可以原生地处理打印。它通过改变滚动区域并仅在滚动区域下方绘制来实现这一点。
由于你提到你正在使用 PyCharm。在 Pycharm 终端中存在一个长期存在的 bug,这可能会导致 Enlighten 对你造成问题,但在其他终端中它将正常工作。

0
你的 tqdm 进度条可能会因为 Python 循环和结构的原因而显示不完整。如果循环结构正确,这种情况不应该发生,但是创建这样的循环并不总是可能的,特别是在使用第三方库时。
在操作完成后,你可以检查进度条的未填充部分,并调用 update() 来更新剩余的进度。
progress_bar.update(progress_bar.total - progress_bar.n)

这似乎是最干净的工作方法,尤其是在使用各种Jupyter笔记本和基于HTML的进度条时。

0

也许尝试一下叫做的函数

tqdm.clear

但是...在此之前先创建一个对象或者...

我会展示给你代码:

from tqdm import tqdm
bar = tqdm(yourList)
for i in bar:
    #Do Stuff :)
    #But Right Where You Wanna Make It Go And Come Back Do This :-
    bar.clear()

希望它能工作 :)


当您在循环内部不调用print时,clear方法可以正常工作。如果您这样做了,您仍然会遇到像OP报告的奇怪行为。 - ZaydH

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