tqdm 源自阿拉伯语 taqaddum (تقدّم),意为“进步”,
也是西班牙语“我非常爱你” (te quiero demasiado) 的缩写。
立即为你的循环添加一个智能进度提示条——只需将任何
可迭代对象包装在 tqdm(iterable) 中,即可完成!
from tqdm import tqdm
for i in tqdm(range(10000)):
...76%|████████████████████████ | 7568/10000 [00:33<00:10, 229.00it/s]
trange(N) 也可以作为 tqdm(range(N)) 的便捷缩写。
它也可以作为模块通过管道执行:
$ seq 9999999 | tqdm --bytes | wc -l
75.2MB [00:00, 217MB/s]
9999999
$ tar -zcf - docs/ | tqdm --bytes --total `du -sb docs/ | cut -f1` \
> backup.tgz
32%|██████████▍ | 8.89G/27.9G [00:42<01:31, 223MB/s]开销很低——每次迭代约 60ns (使用 tqdm.gui 时为 80ns),并经过性能回归的单元测试。
相比之下,已成熟的
ProgressBar
有 800ns/iter 的开销。
除了低开销外,tqdm 还使用智能算法来预测
剩余时间并跳过不必要的迭代显示,这使得
在大多数情况下开销可以忽略不计。
tqdm 可在任何平台
(Linux、Windows、Mac、FreeBSD、NetBSD、Solaris/SunOS)
的任何控制台或 GUI 中运行,并且与 IPython/Jupyter notebook 兼容。
tqdm 无需任何依赖项 (甚至不需要 curses!),只需
Python 和支持 回车符 \r 和
换行符 \n 控制字符的环境。
pip install tqdm拉取并安装预发布版本 devel 分支:
pip install "git+https://github.com/tqdm/tqdm.git@devel#egg=tqdm"conda install -c conda-forge tqdm您可以选择 3 个通道:
snap install tqdm # 隐含 --stable,即最新的标记发布版本
snap install tqdm --candidate # master 分支
snap install tqdm --edge # devel 分支请注意,snap 二进制文件仅用于 CLI 使用(不可 import),并且
会自动设置 bash 标签补全。
docker pull tqdm/tqdm
docker run -i --rm tqdm/tqdm --help还有其他(非官方)地方可以下载 tqdm,特别是用于 CLI 使用:
所有变更列表可在 GitHub 的 Releases:
,
wiki,或
网站上找到。
tqdm 非常通用,有多种用法。
下面介绍三种主要用法。
将 tqdm() 包裹在任何可迭代对象周围:
from tqdm import tqdm
from time import sleep
text = ""
for char in tqdm(["a", "b", "c", "d"]):
sleep(0.25)
text = text + chartrange(i) 是 tqdm(range(i)) 的一个特殊优化实例:
from tqdm import trange
for i in trange(100):
sleep(0.01)在循环外实例化可以手动控制 tqdm():
pbar = tqdm(["a", "b", "c", "d"])
for char in pbar:
sleep(0.25)
pbar.set_description("Processing %s" % char)使用 with 语句手动控制 tqdm() 更新:
with tqdm(total=100) as pbar:
for i in range(10):
sleep(0.1)
pbar.update(10)如果提供了可选变量 total(或具有 len() 的可迭代对象),
则会显示预测统计数据。
with 也是可选的(您可以直接将 tqdm() 赋给一个变量,
但这种情况下不要忘记在结束时 del 或 close()):
pbar = tqdm(total=100)
for i in range(10):
sleep(0.1)
pbar.update(10)
pbar.close()也许 tqdm 最棒的用法是在脚本或命令行中。只需在管道之间插入 tqdm
(或 python -m tqdm)即可将所有 stdin 传递到 stdout,
同时将进度打印到 stderr。
下面的例子演示了计算当前目录中所有 Python 文件的行数,并包含计时信息。
$ time find . -name '*.py' -type f -exec cat \{} \; | wc -l
857365
real 0m3.458s
user 0m0.274s
sys 0m3.325s
$ time find . -name '*.py' -type f -exec cat \{} \; | tqdm | wc -l
857366it [00:03, 246471.31it/s]
857365
real 0m3.585s
user 0m0.862s
sys 0m3.358s请注意,通常 tqdm 的参数也可以指定。
$ find . -name '*.py' -type f -exec cat \{} \; |
tqdm --unit loc --unit_scale --total 857366 >> /dev/null
100%|█████████████████████████████████| 857K/857K [00:04<00:00, 246Kloc/s]正在备份一个大目录?
$ tar -zcf - docs/ | tqdm --bytes --total `du -sb docs/ | cut -f1` \
> backup.tgz
44%|██████████████▊ | 153M/352M [00:14<00:18, 11.0MB/s]可以进一步美化:
$ BYTES=$(du -sb docs/ | cut -f1)
$ tar -cf - docs/ \
| tqdm --bytes --total "$BYTES" --desc Processing | gzip \
| tqdm --bytes --total "$BYTES" --desc Compressed --position 1 \
> ~/backup.tgz
Processing: 100%|██████████████████████| 352M/352M [00:14<00:00, 30.2MB/s]
Compressed: 42%|█████████▎ | 148M/352M [00:14<00:19, 10.9MB/s]或者按文件级别使用 7-zip:
$ 7z a -bd -r backup.7z docs/ | grep Compressing \
| tqdm --total $(find docs/ -type f | wc -l) --unit files \
| grep -v Compressing
100%|██████████████████████████▉| 15327/15327 [01:00<00:00, 712.96files/s]已存在的 CLI 程序已经输出了基本进度信息,可以通过 tqdm 的 --update 和 --update_to 标志来受益:
$ seq 3 0.1 5 | tqdm --total 5 --update_to --null
100%|████████████████████████████████████| 5.0/5 [00:00<00:00, 9673.21it/s]
$ seq 10 | tqdm --update --null # 1 + 2 + ... + 10 = 55 iterations
55it [00:00, 90006.52it/s]最常见的问题与在多行上输出过多的内容有关,而不是一个整洁的单行进度条。
- 一般控制台:需要支持回车符(
CR,\r)。- 某些不支持
\r的云日志控制台 (CloudWatch, K8s)可以尝试export TQDM_POSITION=-1。
- 某些不支持
- 嵌套进度条:
- Unicode:
- 报告支持 Unicode 的环境将具有实心平滑的进度条。备选方案是仅限
ascii的进度条。 - Windows 控制台通常只部分支持 Unicode,因此 通常需要显式指定 ascii=True (也这里)。这是因为 正常宽度的 Unicode 字符被错误地显示为“宽”,或者某些 Unicode 字符无法渲染。
- 报告支持 Unicode 的环境将具有实心平滑的进度条。备选方案是仅限
- 包装生成器:
- 生成器包装函数往往会隐藏可迭代对象的长度。
tqdm不会。 - 将
tqdm(enumerate(...))替换为enumerate(tqdm(...))或tqdm(enumerate(x), total=len(x), ...)。 这同样适用于numpy.ndenumerate。 - 将
tqdm(zip(a, b))替换为zip(tqdm(a), b)或甚至zip(tqdm(a), tqdm(b))。 - 这同样适用于
itertools。 - 一些有用的方便函数可以在
tqdm.contrib下找到。
- 生成器包装函数往往会隐藏可迭代对象的长度。
- Docker-compose 中没有中间输出:
使用
docker-compose run而不是docker-compose up和tty: true。 - 通过环境变量覆盖默认值:
例如,在 CI/云作业中,
export TQDM_MININTERVAL=5以避免日志刷屏。 此覆盖逻辑由tqdm.utils.envwrap装饰器处理 (独立于tqdm很有用)。
class tqdm():
"""
装饰一个可迭代对象,返回一个迭代器,该迭代器与原始可迭代对象完全相同,
但每次请求一个值时都会显示一个动态更新的
进度条。
"""
@envwrap("TQDM_") # 通过环境变量覆盖默认值
def __init__(self, iterable=None, desc=None, total=None, leave=True,
file=`None`, ncols=None, mininterval=0.1,
maxinterval=10.0, miniters=None, ascii=None, disable=False,
unit='it', unit_scale=False, dynamic_ncols=False,
smoothing=0.3, bar_format=None, initial=0, position=None,
postfix=None, unit_divisor=1000, write_bytes=False,
lock_args=None, nrows=None, colour=None, delay=0):- iterable : 可迭代对象,可选
用于装饰进度条的可迭代对象。 留空以手动管理更新。
- desc : 字符串,可选
进度条的前缀。
- total : 整数或浮点数,可选
预期的迭代次数。如果未指定, 则在可能的情况下使用 len(iterable)。如果为 float("inf") 或作为最后 手段,则只显示基本进度统计信息 (无 ETA,无进度条)。 如果
gui为 True 且此参数需要后续更新, 则指定一个任意大的正数作为初始值, 例如 9e9。
- leave : 布尔值,可选
如果在迭代结束时(默认:True),则保留进度条的 所有痕迹。 如果为
None,则仅在position为0时保留。
- file :
io.TextIOWrapper或io.StringIO,可选 指定输出进度消息的位置 (默认:sys.stderr)。使用
file.write(str)和file.flush()方法。有关编码,请参阅write_bytes。
- file :
- ncols : 整数,可选
整个输出消息的宽度。如果指定, 则动态调整进度条的宽度以保持在此范围内。 如果未指定,则尝试使用环境宽度。 备用方案是仪表宽度为 10,计数器和 统计信息无限制。如果为 0,则不打印任何仪表(仅统计信息)。
- mininterval : 浮点数,可选
最小进度显示更新间隔 [默认值: 0.1] 秒。
- maxinterval : 浮点数,可选
最大进度显示更新间隔 [默认值: 10] 秒。 在长时间显示更新延迟后,会自动调整
miniters以使其对应于mininterval。仅在启用了dynamic_miniters或监视线程时有效。
- miniters : 整数或浮点数,可选
最小进度显示更新间隔,以迭代次数为单位。 如果为 0 且
dynamic_miniters为 True,则会自动调整为等于mininterval(CPU 效率更高,适用于严格循环)。 如果大于 0,则会跳过指定数量的迭代显示。 调整此项和mininterval以获得高效循环。 如果进度不稳定,同时存在快速和慢速迭代(网络、跳过项等),您应将 miniters 设置为 1。
- ascii : 布尔值或字符串,可选
如果未指定或为 False,则使用 Unicode(平滑块)填充 仪表。备用方案是使用 ASCII 字符 " 123456789#”。
- disable : 布尔值,可选
是否禁用整个进度条包装器 [默认值:False]。如果设置为 None,则在非 TTY 上禁用。
- unit : 字符串,可选
用于定义每次迭代的单位的字符串 [默认值:it]。
- unit_scale : 布尔值或整数或浮点数,可选
如果为 1 或 True,则迭代次数将自动减少/缩放, 并添加国际单位制标准下的度量前缀 (kilo、mega 等)[默认值:False]。如果为任何其他非零 数字,将缩放
total和n。
- dynamic_ncols : 布尔值,可选
如果设置为 True,则不断将
ncols和nrows更改为 环境(允许窗口调整大小)[默认值:False]。
- smoothing : 浮点数,可选
速度估计的指数加权移动平均平滑因子 (GUI 模式下忽略)。范围从 0(平均速度)到 1 (当前/瞬时速度)[默认值:0.3]。
- bar_format : 字符串,可选
指定自定义条形字符串格式。可能会影响性能。 [默认值:'{l_bar}{bar}{r_bar}'], 其中 l_bar='{desc}: {percentage:3.0f}%|' 和 r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' '{rate_fmt}{postfix}]' 可能的变量:l_bar, bar, r_bar, n, n_fmt, total, total_fmt, percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, rate, rate_fmt, rate_noinv, rate_noinv_fmt, rate_inv, rate_inv_fmt, postfix, unit_divisor, remaining, remaining_s, eta。 请注意,如果 {desc} 为空,则在其后的 ": " 会被自动删除。
- initial : 整数或浮点数,可选
初始计数器值。在重新启动进度条时很有用 [默认值:0]。如果使用浮点数,请考虑在
bar_format中指定{n:.3f}或类似项,或指定unit_scale。
- position : 整数,可选
指定打印此条的行偏移量(从 0 开始) 自动确定(如果未指定)。 对于同时管理多个条(例如,来自线程)很有用。
- postfix : 字典或
*,可选 指定要显示在条末尾的附加统计信息。 如果可能,调用
set_postfix(**postfix)(字典)。
- postfix : 字典或
- unit_divisor : 浮点数,可选
[默认值:1000],仅在
unit_scale为 True 时才有效。
- write_bytes : 布尔值,可选
是否写入字节。如果为 False(默认值),则写入 Unicode。
- lock_args : 元组,可选
传递给
refresh以进行中间输出 (初始化、迭代和更新)。
- nrows : 整数,可选
屏幕高度。如果指定,则从此边界 外的嵌套条隐藏。如果未指定,则尝试使用环境高度。 备用方案是 20。
- colour : 字符串,可选
条的颜色(例如,'green','#00ff00')。
- delay : 浮点数,可选
在过去的 [默认值:0] 秒内不要显示。
- delim : 字符, 可选
- 分隔字符 [默认值: 'n']。使用 '0' 表示 null。 注意:在 Windows 系统上,Python 会将 'n' 转换为 'rn'。
- buf_size : 整数, 可选
- 字符串缓冲区大小(字节) [默认值: 256]。
当指定
delim时使用。
- bytes : 布尔值, 可选
- 如果为 True,则计算字节数,忽略
delim,并默认 将unit_scale设置为 True,unit_divisor设置为 1024,unit设置为 'B'。
- tee : 布尔值, 可选
- 如果为 True,则将
stdin同时传递给stderr和stdout。
- update : 布尔值, 可选
- 如果为 True,则将输入视为新完成的迭代次数,
即传递给
update()的数字。请注意,这很慢 (约 2e5 it/s),因为每个输入都必须解码为数字。
- update_to : 布尔值, 可选
- 如果为 True,则将输入视为总共完成的迭代次数,
即赋值给
self.n的数字。请注意,这很慢 (约 2e5 it/s),因为每个输入都必须解码为数字。
- null : 布尔值, 可选
- 如果为 True,则丢弃输入(无 stdout)。
- manpath : 字符串, 可选
- 安装 tqdm man 手册的目录。
- comppath : 字符串, 可选
- 放置 tqdm 补全文件的目录。
- log : 字符串, 可选
- CRITICAL|FATAL|ERROR|WARN(ING)|[默认值: 'INFO']|DEBUG|NOTSET。
- out : 装饰后的迭代器。
class tqdm():
def update(self, n=1):
"""
手动更新进度条,适用于读取文件的流等情况。
例如:
>>> t = tqdm(total=filesize) # 初始化
>>> for current_buffer in stream:
... ...
... t.update(len(current_buffer))
>>> t.close()
强烈推荐最后一行,但如果 ``t.update()`` 的调用方式使得
``filesize`` 被精确达到并打印,则可能不是必需的。
参数
----------
n : 整数或浮点数, 可选
要添加到内部迭代计数器中的增量
[默认值: 1]。如果使用浮点数,请考虑在 ``bar_format`` 中
指定 ``{n:.3f}`` 或类似格式,或指定 ``unit_scale``。
返回值
-------
out : 布尔值或 None
如果触发了 ``display()``,则为 True。
"""
def close(self):
"""清理并(如果 leave=False)关闭进度条。"""
def clear(self, nomove=False):
"""清除当前进度条显示。"""
def refresh(self):
"""
强制刷新此进度条的显示。
参数
----------
nolock : 布尔值, 可选
如果为 ``True``,则不锁定。
如果 [默认值: ``False``]:调用内部锁的 ``acquire()``。
lock_args : 元组, 可选
传递给内部锁的 ``acquire()``。
如果指定,则仅在 ``acquire()`` 返回 ``True`` 时才 ``display()``。
"""
def unpause(self):
"""从上次打印时间重新启动 tqdm 定时器。"""
def reset(self, total=None):
"""
重置为 0 次迭代,以便重复使用。
考虑与 ``leave=True`` 结合使用。
参数
----------
total : 整数或浮点数, 可选。用于新进度条的总数。
"""
def set_description(self, desc=None, refresh=True):
"""
设置/修改进度条的描述。
参数
----------
desc : 字符串, 可选
refresh : 布尔值, 可选
强制刷新 [默认值: True]。
"""
def set_postfix(self, ordered_dict=None, refresh=True, **tqdm_kwargs):
"""
设置/修改后缀(附加统计信息)
并根据数据类型自动格式化。
参数
----------
ordered_dict : 字典或 OrderedDict, 可选
refresh : 布尔值, 可选
强制刷新 [默认值: True]。
kwargs : 字典, 可选
"""
@classmethod
def write(cls, s, file=sys.stdout, end="\n"):
"""通过 tqdm 打印消息(避免与进度条重叠)。"""
@property
def format_dict(self):
"""公共 API,用于只读成员访问。"""
def display(self, msg=None, pos=None):
"""
使用 ``self.sp`` 在指定的 ``pos`` 位置显示 ``msg``。
考虑在继承时重载此函数,例如使用:
``self.some_frontend(**self.format_dict)`` 而不是 ``self.sp``。
参数
----------
msg : 字符串, 可选。要显示的内容(默认值:``repr(self)``)。
pos : 整数, 可选。要 ``moveto`` 的位置
(默认值:``abs(self.pos)``)。
"""
@classmethod
@contextmanager
def wrapattr(cls, stream, method, total=None, bytes=True, **tqdm_kwargs):
"""
stream : 文件类对象。
method : 字符串,"read" 或 "write"。``read()`` 的结果和
``write()`` 的第一个参数应该具有 ``len()``。
>>> with tqdm.wrapattr(file_obj, "read", total=file_obj.size) as fobj:
... while True:
... chunk = fobj.read(chunk_size)
... if not chunk:
... break
"""
@classmethod
def pandas(cls, *targs, **tqdm_kwargs):
"""将当前 `tqdm` 类注册到 `pandas`。"""
def trange(*args, **tqdm_kwargs):
"""`tqdm(range(*args), **tqdm_kwargs)` 的快捷方式。"""def tqdm.contrib.tenumerate(iterable, start=0, total=None,
tqdm_class=tqdm.auto.tqdm, **tqdm_kwargs):
"""等同于 `numpy.ndenumerate` 或内置 `enumerate`。"""
def tqdm.contrib.tzip(iter1, *iter2plus, **tqdm_kwargs):
"""等同于内置 `zip`。"""
def tqdm.contrib.tmap(function, *sequences, **tqdm_kwargs):
"""等同于内置 `map`。"""class tqdm.notebook.tqdm(tqdm.tqdm):
"""IPython/Jupyter Notebook 小部件。"""
class tqdm.auto.tqdm(tqdm.tqdm):
"""自动在 `tqdm.notebook` 和 `tqdm.tqdm` 之间进行选择。"""
class tqdm.asyncio.tqdm(tqdm.tqdm):
"""异步版本。"""
@classmethod
def as_completed(cls, fs, *, loop=None, timeout=None, total=None,
**tqdm_kwargs):
"""`asyncio.as_completed` 的包装器。"""
class tqdm.gui.tqdm(tqdm.tqdm):
"""Matplotlib GUI 版本。"""
class tqdm.tk.tqdm(tqdm.tqdm):
"""Tkinter GUI 版本。"""
class tqdm.rich.tqdm(tqdm.tqdm):
"""`rich.progress` 版本。"""
class tqdm.keras.TqdmCallback(keras.callbacks.Callback):
"""用于 epoch 和 batch 进度的 Keras 回调。"""
class tqdm.dask.TqdmCallback(dask.callbacks.Callback):
"""用于 task 进度的 Dask 回调。"""tqdm.contrib 包还包含实验性模块:
tqdm.contrib.itertools: `itertools` 的轻量级包装器tqdm.contrib.concurrent: `concurrent.futures` 的轻量级包装器tqdm.contrib.slack: 发布到 Slack 机器人tqdm.contrib.discord: 发布到 Discord 机器人tqdm.contrib.telegram: 发布到 Telegram 机器人tqdm.contrib.bells: 自动启用所有可选功能auto,pandas,slack,discord,telegram
- 请查看 示例 文件夹;
- 导入模块并运行
help(); - 查阅 wiki;
- 查看 PyData London 的幻灯片,或者
- 运行
。
可以使用 `tqdm` 条的 `desc` 和 `postfix` 参数来显示和动态更新自定义信息:
from tqdm import tqdm, trange
from random import random, randint
from time import sleep
with trange(10) as t:
for i in t:
# 描述将显示在左侧
t.set_description('GEN %i' % i)
# 对其将显示在右侧,
# 根据参数的数据类型自动格式化
t.set_postfix(loss=random(), gen=randint(1,999), str='h',
lst=[1, 2])
sleep(0.1)
with tqdm(total=10, bar_format="{postfix[0]} {postfix[1][value]:>8.2g}",
postfix=["Batch", {"value": 0}]) as t:
for i in range(10):
sleep(0.1)
t.postfix[1]["value"] = i / 2
t.update()使用 `bar_format` 字符串中的 `{postfix[...]} ` 时需要注意的点:
postfix也需要以兼容的格式作为初始参数传递,并且- 如果 `postfix` 是一个类字典对象,它将被自动转换为字符串。要阻止此行为,请在字典中插入一个额外的项,其中键不是字符串。
还可以通过覆盖 `format_dict` 来定义额外的 `bar_format` 参数,并且可以使用 `ascii` 来修改条本身:
from tqdm import tqdm
class TqdmExtraFormat(tqdm):
"""Provides a `total_time` format parameter"""
@property
def format_dict(self):
d = super().format_dict
total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1)
d.update(total_time=self.format_interval(total_time) + " in total")
return d
for i in TqdmExtraFormat(
range(9), ascii=" .oO0",
bar_format="{total_time}: {percentage:.0f}%|{bar}{r_bar}"):
if i == 4:
break00:00 in total: 44%|0000. | 4/9 [00:00<00:00, 962.93it/s]
请注意,{bar} 也支持格式说明符 [width][type]。
width- 未指定(默认):自动填充
ncols int >= 0:固定宽度,覆盖ncols逻辑int < 0:从默认值减去
- 未指定(默认):自动填充
typea:ASCII(覆盖 `ascii=True`)u:Unicode(覆盖 `ascii=False`)b:空白(覆盖 `ascii=" "`)
这意味着可以通过使用以下方式创建具有固定宽度并右对齐文本的进度条:
bar_format="{l_bar}{bar:10}|{bar:-10b}right-justified"
tqdm 支持嵌套进度条。例如:
from tqdm.auto import trange
from time import sleep
for i in trange(4, desc='1st loop'):
for j in trange(5, desc='2nd loop'):
for k in trange(50, desc='3rd loop', leave=False):
sleep(0.01)对于手动控制位置(例如,用于多进程),
您可以指定 position=n,其中 n=0 表示最外层进度条,
n=1 表示下一层,依此类推。
但是,最好先检查 tqdm 是否无需手动 position 即可工作。
from time import sleep
from tqdm import trange, tqdm
from multiprocessing import Pool, RLock, freeze_support
L = list(range(9))
def progresser(n):
interval = 0.001 / (n + 2)
total = 5000
text = f"#{n}, est. {interval * total:<04.2}s"
for _ in trange(total, desc=text, position=n):
sleep(interval)
if __name__ == '__main__':
freeze_support() # for Windows support
tqdm.set_lock(RLock()) # for managing output contention
p = Pool(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),))
p.map(progresser, L)请注意,在 Python 3 中,tqdm.write 是线程安全的:
from time import sleep
from tqdm import tqdm, trange
from concurrent.futures import ThreadPoolExecutor
L = list(range(9))
def progresser(n):
interval = 0.001 / (n + 2)
total = 5000
text = f"#{n}, est. {interval * total:<04.2}s"
for _ in trange(total, desc=text):
sleep(interval)
if n == 6:
tqdm.write("n == 6 completed.")
tqdm.write("`tqdm.write()` is thread-safe in py3!")
if __name__ == '__main__':
with ThreadPoolExecutor() as p:
p.map(progresser, L)tqdm 可以轻松支持回调/钩子和手动更新。
这是一个使用 urllib 的示例:
``urllib.urlretrieve`` 文档
[...]如果存在,钩子函数将在建立网络连接时调用一次,之后每次读取块时调用一次。钩子将接收三个参数:已传输块的数量,块大小(以字节为单位),以及文件的总大小。钩子将接收三个参数;已传输块的数量,块大小(以字节为单位),以及文件的总大小。钩子将接收三个参数;已传输块的数量,块大小(以字节为单位),以及文件的总大小。[...]
import urllib, os
from tqdm import tqdm
urllib = getattr(urllib, 'request', urllib)
class TqdmUpTo(tqdm):
"""提供 `update_to(n)`,它使用 `tqdm.update(delta_n)`。"""
def update_to(self, b=1, bsize=1, tsize=None):
"""
b : int, optional
已传输块数 [默认值: 1]。
bsize : int, optional
每个块的大小(以 tqdm 单位计)[默认值: 1]。
tsize : int, optional
总大小(以 tqdm 单位计)。如果为 [默认值: None],则保持不变。
"""
if tsize is not None:
self.total = tsize
return self.update(b * bsize - self.n) # 也设置 self.n = b * bsize
eg_link = "https://caspersci.uk.to/matryoshka.zip"
with TqdmUpTo(unit='B', unit_scale=True, unit_divisor=1024, miniters=1,
desc=eg_link.split('/')[-1]) as t: # 所有可选参数
urllib.urlretrieve(eg_link, filename=os.devnull,
reporthook=t.update_to, data=None)
t.total = t.n灵感来自 twine#242。 在 examples/tqdm_wget.py 中有函数式替代方案。
建议在迭代速度可能存在较大差异时(例如在信号不稳定的连接上下载文件)使用 miniters=1。
包装 read/write 方法
要测量通过文件类对象的 read 或 write 方法的吞吐量,请使用 CallbackIOWrapper:
从 tqdm.auto 导入 tqdm
从 tqdm.utils 导入 CallbackIOWrapper
与 tqdm(合计=file_obj.大小,
单位='B', 单位比例=True, 单位除数=1024) 作为 t:
fobj = CallbackIOWrapper(t.更新, file_obj, "读取")
当 True:
块 = fobj.读取(块大小)
如果 不 块:
中断
t.重置()
# ... 继续将 `t` 用于其他用途或者,使用更简单的 wrapattr 便利函数,
这将 urllib 和 CallbackIOWrapper 的示例
压缩为:
导入 urllib, os
从 tqdm 导入 tqdm
示例链接 = "https://caspersci.uk.to/matryoshka.zip"
响应 = getattr(urllib, 'request', urllib).urlopen(示例链接)
与 tqdm.wrapattr(open(os.devnull, "wb"), "写入",
最小迭代次数=1, 描述=示例链接.split('/')[-1],
合计=getattr(响应, '长度', None)) 作为 fout:
对于 块 在 响应:
fout.写入(块)requests 的等效代码几乎相同:
导入 requests, os 从 tqdm 导入 tqdm eg_link = "https://caspersci.uk.to/matryoshka.zip" response = requests.get(eg_link, stream=True) with tqdm.wrapattr(open(os.devnull, "wb"), "write", miniters=1, desc=eg_link.split('/')[-1], total=int(response.headers.get('content-length', 0))) as fout: for chunk in response.iter_content(chunk_size=4096): fout.write(chunk)
自定义回调
tqdm 以智能跳过不必要的显示而闻名。要让自定义回调利用这一点,只需使用
update() 的返回值即可。如果触发了 display(),则将其设置为 True。
从 tqdm.auto 导入 tqdm 作为 std_tqdm 定义 外部回调(*args, **kwargs): ... 类 TqdmExt(std_tqdm): 定义 更新(self, n=1): 显示 = super().更新(n) 如果 显示: external_callback(**self.format_dict) 返回 显示
请注意,目前 break 无法被异步迭代器捕获。
这意味着 tqdm 在这种情况下无法清理自身:
从 tqdm.asyncio 导入 tqdm 异步 对于 i 在 tqdm(range(9)): 如果 i == 2: 中断
而是,要么手动调用 pbar.close(),要么使用上下文管理器语法:
from tqdm.asyncio import tqdm
with tqdm(range(9)) as pbar:
async for i in pbar:
if i == 2:
break应广大用户要求,我们增加了对 pandas 的支持——以下是
DataFrame.progress_apply 和 DataFrameGroupBy.progress_apply 的示例:
import pandas as pd
import numpy as np
from tqdm import tqdm
df = pd.DataFrame(np.random.randint(0, 100, (100000, 6)))
# 使用 `tqdm` 注册 `pandas.progress_apply` 和 `pandas.Series.map_apply`
# (可以使用 `tqdm.gui.tqdm`, `tqdm.notebook.tqdm`, 可选的 kwargs 等)
tqdm.pandas(desc="my bar!")
# 现在您可以使用 `progress_apply` 代替 `apply`
# 并使用 `progress_map` 代替 `map`
df.progress_apply(lambda x: x**2)
# 也可以进行 groupby:
# df.groupby(0).progress_apply(lambda x: x**2)如果您对这是如何工作的(以及如何修改它以用于您自己的回调),请参阅
examples
文件夹或导入该模块并运行 help()。
还提供了一个 keras 回调:
from tqdm.keras import TqdmCallback
...
model.fit(..., verbose=0, callbacks=[TqdmCallback()])还提供了一个 dask 回调:
from tqdm.dask import TqdmCallback
with TqdmCallback(desc="compute"):
...
arr.compute()
# 或全局使用回调
cb = TqdmCallback(desc="global")
cb.register()
arr.compute()IPython/Jupyter 通过 tqdm.notebook 子模块支持:
from tqdm.notebook import trange, tqdm
from time import sleep
for i in trange(3, desc='1st loop'):
for j in tqdm(range(100), desc='2nd loop'):
sleep(0.01)除了 tqdm 功能外,该子模块还提供了一个原生的 Jupyter
小部件(兼容 IPython v1-v4 和 Jupyter),完全可用和嵌套的条形图,以及颜色提示(蓝色:正常,绿色:已完成,红色:错误/中断,
浅蓝色:无 ETA);如下所示。
notebook 版本支持百分比或像素来设置整体宽度
(例如:ncols='100%' 或 ncols='480px')。
还可以让 tqdm 自动选择控制台或 notebook 版本,方法是使用
autonotebook 子模块:
from tqdm.autonotebook import tqdm tqdm.pandas()
请注意,如果 autonotebook 在 notebook 中运行,将发出 TqdmExperimentalWarning
,因为它无法区分 jupyter notebook 和 jupyter console。使用
auto 而不是 autonotebook 来抑制此警告。
请注意,notebook 将在创建条形的单元格中显示该条形。 这可能与使用它的单元格不同。 如果不需要这样,请执行以下任一操作:
- 延迟条形的创建到必须显示它的单元格,或者
- 使用
display=False创建条形,然后在后续单元格中调用display(bar.container):
从 tqdm.notebook 导入 tqdm
pbar = tqdm(..., display=False)# 不同的单元格
display(pbar.container)keras 回调函数具有一个 display() 方法,可以进行类似的调用:
从 tqdm.keras 导入 TqdmCallback
cbk = TqdmCallback(display=False)# 不同的单元格
cbk.display()
model.fit(..., verbose=0, callbacks=[cbk])另一种可能性是有一个(在 notebook 顶部附近)不断被重用的单个进度条(使用 reset() 而不是 close())。
因此,notebook 版本(与 CLI 版本不同)在 Exception 时不会自动调用 close()。
从 tqdm.notebook 导入 tqdm
pbar = tqdm()# 不同的单元格
iterable = range(100)
pbar.reset(total=len(iterable)) # 使用新的 `total` 进行初始化
for i in iterable:
pbar.update()
pbar.refresh() # 强制打印最终状态但不 `close()`要更改默认参数(例如,将 dynamic_ncols=True),
只需使用内置的 Python magic 即可:
从 functools 导入 partial
从 tqdm 导入 tqdm 作为 std_tqdm
tqdm = partial(std_tqdm, dynamic_ncols=True)有关进一步的自定义,
可以从 tqdm 继承以创建自定义回调函数(如
上面的 TqdmUpTo 示例)或用于自定义前端
(例如,notebook 或绘图包等 GUI)。 在后一种情况下:
def __init__()调用super().__init__(..., gui=True)来禁用 终端status_printer的创建。- 重新定义:
close()、clear()、display()。
考虑重载 display() 以使用例如
self.frontend(**self.format_dict) 而不是 self.sp(repr(self))。
继承的一些子模块示例:
- tqdm/notebook.py
- tqdm/gui.py
- tqdm/tk.py
- tqdm/contrib/slack.py
- tqdm/contrib/discord.py
- tqdm/contrib/telegram.py
您可以将 tqdm 用作非单调递增的仪表。
这可能是因为 n 减小(例如,CPU 使用率监控器)或者 total
发生变化。
一个例子是递归搜索文件。 total 是到目前为止找到的对象数量,而
n 是这些对象中是文件的数量(而不是文件夹):
从 tqdm 导入 tqdm
导入 os.path
定义 find_files_recursively(path, show_progress=True):
files = []
# total=1 假设 `path` 是一个文件
t = tqdm(total=1, unit="file", disable=not show_progress)
如果 not os.path.exists(path):
引发 IOError("找不到:" + path)
定义 append_found_file(f):
files.append(f)
t.update()
定义 list_found_dir(path):
"""假设 os.path.isdir(path) 返回 os.listdir(path)"""
listing = os.listdir(path)
# 减去 1,因为我们找到的一个“文件”实际上是这个目录
t.total += len(listing) - 1
# 获得信息的技巧,而无需强制刷新
t.set_postfix(dir=path[-10:], refresh=False)
t.update(0) # 可能会触发刷新
返回 listing
定义 recursively_search(path):
如果 os.path.isdir(path):
对于 f 在 list_found_dir(path):
recursively_search(os.path.join(path, f))
否则:
append_found_file(path)
recursively_search(path)
t.set_postfix(dir=path)
t.close()
返回 files使用 update(0) 是一个方便的方法,可以让 tqdm 决定何时触发显示刷新,以避免控制台刷屏。
这是一个进行中的工作(请参阅 #737)。
由于 tqdm 使用简单的打印机制来显示进度条,
因此在进度条打开时,您不应该使用 print() 在终端中写入任何消息。
要在终端中写入消息而不与 tqdm 进度条显示发生冲突,
提供了一个 .write() 方法:
from tqdm.auto import tqdm, trange
from time import sleep
bar = trange(10)
for i in bar:
# 使用 tqdm 类方法 .write() 打印
sleep(0.1)
if not (i % 3):
tqdm.write("Done task %i" % i)
# 也可以使用 bar.write()默认情况下,这将打印到标准输出 sys.stdout,但是您
可以使用 file 参数指定任何类文件对象。例如,这
可以用于将写入的消息重定向到日志文件或类。
如果使用可以向控制台打印消息的库,编辑该库
并将 print() 替换为 tqdm.write() 可能不是理想的选择。
在这种情况下,将 sys.stdout 重定向到 tqdm.write() 是一个选项。
要重定向 sys.stdout,请创建一个类文件对象,
它将任何输入字符串写入 tqdm.write(),并提供
file=sys.stdout, dynamic_ncols=True 参数。
下面提供一个可重用的标准示例:
from time import sleep
import contextlib
import sys
from tqdm import tqdm
from tqdm.contrib import DummyTqdmFile
@contextlib.contextmanager
def std_out_err_redirect_tqdm():
orig_out_err = sys.stdout, sys.stderr
try:
sys.stdout, sys.stderr = map(DummyTqdmFile, orig_out_err)
yield orig_out_err[0]
# 传递异常
except Exception as exc:
raise exc
# 始终在需要时恢复 sys.stdout/err
finally:
sys.stdout, sys.stderr = orig_out_err
def some_fun(i):
print("Fee, fi, fo,".split()[i])
# 将 stdout 重定向到 tqdm.write() (不要忘记 `as save_stdout`)
with std_out_err_redirect_tqdm() as orig_stdout:
# tqdm 需要原始 stdout
# 并且 dynamic_ncols=True 以自动检测控制台宽度
for i in tqdm(range(3), file=orig_stdout, dynamic_ncols=True):
sleep(.5)
some_fun(i)
# 在 `with` 语句之后,打印功能已恢复
print("Done!")与上面详细介绍的 sys.stdout/sys.stderr 类似,控制台 logging
也可以重定向到 tqdm.write()。
警告:如果也重定向了 sys.stdout/sys.stderr,请确保
首先重定向 logging(如果需要)。
在 tqdm.contrib.logging 中提供了辅助方法。例如:
import logging
from tqdm import trange
from tqdm.contrib.logging import logging_redirect_tqdm
LOG = logging.getLogger(__name__)
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
with logging_redirect_tqdm():
for i in trange(9):
if i == 4:
LOG.info("console logging redirected to `tqdm.write()`")
# logging 已恢复tqdm 实现了一些技巧来提高效率并减少开销。
- 避免不必要频繁的进度条刷新:
mininterval定义了每次刷新之间 等待的时间。tqdm始终在后台进行更新, 但它只会在每mininterval才会显示。 - 减少访问系统时钟/时间的调用次数。
mininterval比miniters更直观。 一个巧妙的调整系统dynamic_miniters将会自动调整miniters以适应能够在mininterval时间内完成的迭代次数。 本质上,tqdm会检查是否到了打印更新的时间,而无需实际检查时间。 但通过手动设置miniters仍可绕过此行为。
但是,请考虑一个快速和慢速迭代混合的情况。
在几次快速迭代后,dynamic_miniters 会将 miniters 设置为一个
较大的值。当迭代速率随后变慢时,miniters 将
保持较大值,从而降低显示更新频率。为解决此问题:
maxinterval定义了两次显示刷新之间的最大时间。 一个并发监视线程会检查是否已过期的更新,并在必要时强制进行一次更新。
监视线程不应产生明显的开销,并默认确保至少每 10 秒更新一次。
可以通过设置任何 tqdm 实例的 monitor_interval 来直接更改此值
(例如,t = tqdm.tqdm(...); t.monitor_interval = 2)。
可以通过在实例化任何 tqdm 进度条之前将
tqdm.tqdm.monitor_interval 设置为 0 来禁用整个应用程序的监视线程。
您现在可以购买 tqdm 品牌周边商品!
所有源代码都托管在 GitHub 上。 欢迎贡献。
请参阅 CONTRIBUTING 文件以获取更多信息。
为表示对项目做出重大贡献的开发者,按
SLoC(存活代码行数,
git fame -wMC --excl '\.(png|gif|jpg)$')
排名如下:
| 姓名 | ID | SLoC | 备注 |
|---|---|---|---|
| Casper da Costa-Luis | casperdcl | ~80% | 主要维护者 |
| Stephen Larroque | lrq3000 | ~9% | 团队成员 |
| Martin Zugnoni | martinzugnoni | ~3% | |
| Daniel Ecer | de-code | ~2% | |
| Richard Sheridan | richardsheridan | ~1% | |
| Guangshuo Chen | chengs | ~1% | |
| Helio Machado | 0x2b3bfa0 | ~1% | |
| Kyle Altendorf | altendky | <1% | |
| Noam Yorav-Raphael | noamraph | <1% | 原作者 |
| Matthew Stevens | mjstevens777 | <1% | |
| Hadrien Mary | hadim | <1% | 团队成员 |
| Mikhail Korobov | kmike | <1% | 团队成员 |
可以在 这个 wiki 页面 上找到相关列表。







