Python制作进度条,原来有这么多方法!
我先不整那些虚的哈,直接说个场景你肯定熟。
就那种:你写了个脚本,处理十几万个文件、搞数据清洗、跑模型训练,命令行敲下去之后,屏幕一闪……就没动静了。风扇呼呼转,你也不知道它是卡死了,还是在老老实实干活。 昨天晚上我在公司楼下奶茶店等脚本跑,盯着黑乎乎的终端,真有点想给它安个“心电图监护仪”,于是就顺手把几种 Python 进度条的写法捋了一遍。
结果一看,哎哟,方法还真不少。
先来点儿最朴素的:print 土味进度条
最原始的办法其实谁都会,就是不停地 print。 但稍微讲究一点,我们可以用 \r 回车符,把光标拉回一行开头,覆盖原来的内容,看起来就像一条在动的进度条。
import time
import sysfor i in range(101):
# \r 回到行首,end="" 不换行
sys.stdout.write(f"\r进度:{i:3d}%")
sys.stdout.flush()
time.sleep(0.05)
print("\n任务完成!")
这段跑一下,你就会看到命令行上只占一行,数字从 0% 慢慢涨到 100%。 几个小点顺手说一下:
\r是“回车但不换行”,光标回到行首sys.stdout.flush()是强制把缓冲区的内容推到终端,不然有可能几次才刷新一次f"{i:3d}%"是把数字格式化成宽度 3 的整数,比如1、10、100
这个东西已经能解决 80% 的焦虑:至少知道程序还在跑。
再高级一点:自己封一个小进度条函数
如果你想来点“条条”,比如 [####------] 40% 这种,可以自己包一个小函数,写一次处处用。
import sys
import timedefprogress_bar(curr, total, bar_len=30):
percent = curr / total
filled = int(bar_len * percent)
bar = "#" * filled + "-" * (bar_len - filled)
sys.stdout.write(f"\r[{bar}] {percent*100:6.2f}% ({curr}/{total})")
sys.stdout.flush()
if __name__ == "__main__":
total = 200
for i in range(1, total + 1):
# 假装在干活
time.sleep(0.02)
progress_bar(i, total)
print("\n全部搞定!")
你可以想象一下日常脚本里这么用:
扫描一个目录下所有文件,每处理完一个文件就 progress_bar(done, total)跑数据库迁移,一条一条插,顺便看看还要等多久 训练模型的时候,外面再套一层 epoch 级别的进度,每个 epoch 里再来个 batch 级别的(当然这个就有点花了)
这个自定义函数的好处就是轻量、不用装库,啥环境都能跑。
懒人福音:tqdm,直接一行搞定
说实话,自己写进度条写两次你就会嫌烦…… Python 圈子最出名的进度条库就是 tqdm,名字看着怪,其实就是 “进度条” 那意思。用起来非常不费脑子。
先装一下:
pip install tqdm
最常见的用法,直接包在 for 外面:
from tqdm import tqdm
import timefor i in tqdm(range(1000), desc="处理数据"):
# 模拟耗时操作
time.sleep(0.001)
跑起来你会看到一条很专业的进度条,里面自带:
当前进度百分比 已用时间、预计剩余时间 每秒迭代次数
基本上你能想到的信息它都给你弄好了。
如果你不是简单的 range,而是一个列表、生成器啥的,也可以直接包:
from tqdm import tqdmitems = ["a.txt", "b.txt", "c.txt"]
for name in tqdm(items, desc="读取文件"):
# 在这里处理 name
pass
还有一种场景是总量你知道,但循环不是按“列表”写的,比如你在 while 里下载文件、或者不停从队列里取任务,这时候可以用“手动更新”的写法:
from tqdm import tqdm
import timetotal = 100
with tqdm(total=total, desc="下载中") as bar:
for i in range(total):
# 假装每次下载一块
time.sleep(0.03)
bar.update(1) # 手动 +1
这个 with 写法有个好处:进度条结束后它会帮你把光标换到下一行,不会把后面的日志写到同一行上。
想要更好看一点:rich 的花式进度条
有时候你跑的不只是一个循环,比如同时有“下载文件”“解析数据”“写入数据库”三件事,你就会开始嫌普通进度条太简陋。 这时候可以考虑下 rich 这个库,既能画颜色、表格、提示框,也有挺强的进度条。
先装:
pip install rich
如果你只是想来个和 tqdm 差不多的体验,可以直接用它内置的 track:
from time import sleep
from rich.progress import trackfor i in track(range(100), description="Rich 进度条在跑..."):
sleep(0.03)
稍微复杂一点,可以玩多任务进度条:
from time import sleep
from rich.progress import Progresswith Progress() as progress:
task_download = progress.add_task("下载文件", total=100)
task_parse = progress.add_task("解析数据", total=200)
whilenot progress.finished:
ifnot progress.finished:
progress.update(task_download, advance=1)
progress.update(task_parse, advance=2)
sleep(0.05)
你会看到终端里两条进度条一起跑,看起来就很有那种“小型监控面板”的感觉。rich 的优势主要是:
多任务、多行进度条 自带颜色、样式,很适合做一点“可视化” 和它自己的日志、表格、Traceback 能组合在一起用
如果你是在给团队做一个 CLI 工具,想让别人一看就觉得“哎呦,这玩意还挺专业”,rich 很值得上。
说一点我们经常会碰到的:下载文件。 如果你用 requests 去下一个大文件,没进度条的时候就只能干等,体验很迷。
换成 tqdm 加一点小逻辑,就能看着它慢慢涨:
import requests
from tqdm import tqdmdefdownload_file(url, filename):
with requests.get(url, stream=True) as r:
r.raise_for_status()
total = int(r.headers.get("content-length", 0))
chunk_size = 1024
with open(filename, "wb") as f, tqdm(
total=total,
unit="B",
unit_scale=True,
unit_divisor=1024,
desc=f"下载 {filename}"
) as bar:
for chunk in r.iter_content(chunk_size=chunk_size):
ifnot chunk:
continue
f.write(chunk)
bar.update(len(chunk))
if __name__ == "__main__":
download_file("https://example.com/big-file.zip", "big-file.zip")
这里几个点顺手说说:
stream=True是流式下载,不一次性把文件全读到内存content-length是服务端告诉你的总字节数,有这个才能画“准一点”的进度条bar.update(len(chunk))就是根据真实下载字节数更新进度
这个套路改一改其实也能用在:
大批量导出导入数据(按行更新进度) 读一个超大的 CSV,每处理一行就 update(1)Kafka / MQ 消费消息时,做一个简单的“今日消费多少条”的监控
这个是真实踩过的坑哈: 你在一边用 tqdm 画进度条,一边 print 或 logging.info 打日志,结果屏幕上花里胡哨一大堆,进度条还被打断成好几段,看着就头大。
几个小建议:
日志尽量打文件里,进度条留给终端
logging配个FileHandler,终端就让进度条独占。如果是批量脚本,不太需要保留进度条,可以让它结束后“擦掉”:
for x in tqdm(range(100), desc="处理中", leave=False):
...leave=False的意思就是结束后别留下那条进度条。在 CI / 日志系统里跑脚本,没必要画进度条 这种环境下你可以加个参数控制,比如:
defiter_with_progress(it, use_tqdm=True):
if use_tqdm:
from tqdm import tqdm
return tqdm(it)
return it
随便碎碎念下我的个人习惯,你可以对照自己的场景选一个:
写个一次性的小脚本,本地跑跑: 随手用
\r+print或者自己那个progress_bar函数就够了日常长期用的 CLI 工具、数据处理脚本: 直接上
tqdm,几乎零成本,还自带 ETA、吞吐量想做得好看一点、任务又比较多: 用
rich的Progress,多条进度条一起跑,效果不错和网络、IO 强相关的长任务(下载/上传/同步): 推荐
tqdm的手动更新写法,按真实处理的字节/记录数更新,更靠谱
反正一句话:别再让用户(包括未来的你自己)在黑漆漆的终端前干等了,随便选一个进度条加上去,成本低,体验提升特别明显。
我这边今天就先到这儿,等下还得改一个线上脚本的日志输出,不然进度条又要被打断了…
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB。