使用 tqdm 在 Python 应用程序中显示进度

如果您的程序需要一段时间才能显示结果,请通过显示其正在取得的进度来避免用户感到沮丧。
88 位读者喜欢这个。
arrows cycle symbol for failing faster

Opensource.com

闪米特语词根 q-d-m 在阿拉姆语、希伯来语和阿拉伯语中通常与前进或取得进步有关。阿拉伯语单词 taqaddum (تقدّم) 意为“进步”。进步很重要。正如每一部感觉良好的电影都会告诉你的那样,旅程与目的地一样重要。

大多数程序都有一个明确的目标,一个期望的最终状态。有时,计算最终状态可能需要很长时间。虽然计算机不在乎,没有感情,但人们却在乎。人类不喜欢坐着等待而没有任何可见的进度迹象。怀疑悄然而生。程序崩溃了吗?磁盘在猛烈地抖动吗?操作系统是否将其所有计算资源都分配给了其他任务?

就像正义一样,进步必须被看到,而不仅仅是被完成。tqdm Python 库有助于使进度显而易见。

tqdm 模块与控制台配合使用,但它也特别支持我最喜欢的环境之一:Jupyter。要在 Jupyter 中使用 tqdm,您需要导入 notebook 子模块并安装 ipywidgetsnotebook 子模块与 tqdm 接口兼容。

这意味着您可以进行一些导入时的恶作剧,以导入正确的模块,同时保持 tqdm 的用法不变。诀窍是检查 __main__ 模块是否具有全局变量 get_ipython。虽然这是一种启发式方法,但它相当准确

import sys
if hasattr(sys.modules["__main__"], "get_ipython"):
    from tqdm import notebook as tqdm
else:
    import tqdm

最简单的情况是,当某些事情需要运行一定次数的迭代(提前已知)时,并且每次迭代花费的时间大致相同。例如,有一种算法可以通过从 1 作为猜测开始,然后计算改进的猜测来计算任何数字的平方根

def improve_guess(rt, n):
    return (rt + n/rt) / 2

少量改进就可以使您非常接近。例如,您可以计算 2 的平方根

guess = 1
target = 2
for i in tqdm.trange(10):
    guess = improve_guess(guess, target)

它精确到小数点后 10 位!

round(2 - guess*guess, 10)
0.0

一个稍微复杂的例子是,当元素的数量已知,并且处理每个元素花费的时间相似时。例如,您可以计算一些数字的乘积。为此,您需要一些随机数

import random
numbers = [random.uniform(0, 2.8) for i in range(100)]
numbers[:5]
[2.6575636572230916,
0.1286674965830302,
1.0634250104041332,
1.1760969844376505,
0.45192978568125486]

现在数字已经输入,是时候将它们相乘了。使用 tqdm 最简单的方法是包装一个 Python 可迭代对象。这些值将是相同的,但 tqdm 也会显示一个进度条

result = 1
for num in tqdm.tqdm(numbers):
    result *= num
result
2.4081854901728303

然而,并非所有事情都是可预测的。最不可预测的事情之一是网络速度。当您下载一个大文件时,衡量进度的唯一方法是显式检查已下载了多少

url = "https://pythonlang.cn/ftp/python/3.9.0/Python-3.9.0.tgz"
import httpx
with httpx.stream("GET", url) as response:
    total = int(response.headers["Content-Length"])
    with tqdm.tqdm(total=total) as progress:
        for chunk in response.iter_bytes():
            progress.update(len(chunk))

有时,嵌套进度条是有意义的。例如,如果您正在下载一个目录,您将需要一个跟踪文件的进度条和一个每个文件的进度条。

这是一个示例(但实际上没有下载目录)

files = [f"vid-{i}.mp4" for i in range(4)]
for fname in tqdm.tqdm(files, desc="files"):
    total = random.randrange(10**9, 2 * 10**9)
    with tqdm.tqdm(total=total, desc=fname) as progress:
        current = 0
        while current < total:
            chunk_size = min(random.randrange(10**3, 10**5), total - current)
            current += chunk_size
            if random.uniform(0, 1) < 0.01:
                time.sleep(0.1)
            progress.update(chunk_size)

因此,如果您的程序需要一段时间才能显示最终结果,请避免让用户感到沮丧:显示它正在取得的进度!

接下来阅读什么
标签
Moshe sitting down, head slightly to the side. His t-shirt has Guardians of the Galaxy silhoutes against a background of sound visualization bars.
Moshe 自 1998 年以来一直参与 Linux 社区,在 Linux“安装派对”中提供帮助。他自 1999 年以来一直在编写 Python 程序,并为核心 Python 解释器做出了贡献。Moshe 自这些术语出现之前就一直是 DevOps/SRE,非常关心软件可靠性、构建可重现性以及其他此类事情。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.