这两天写我的BSqlier的时候,遇到很多问题,其中有一个就是增加进度条的时候遇到很多很多问题,用的也就是tqdm,那没办法,分析下源码吧…
安装tqdm
没什么可说的
1 | pip install tqdm |
当然也可以安装最新的开发版
1 | pip install -e git+https://github.com/tqdm/tqdm.git@master#egg=tqdm |
documentation
首先是官方文档
https://pypi.python.org/pypi/tqdm
但是官方文档有很多错误的代码和示范…不知道为什么,那么就根据源码来看吧
源码分析
在分析源码之前
在分析源码之前,我们首先应该看看这个模块的使用方式
自动控制进度更新
首先最基本的用法
1 | from tqdm import tqdm |
通过一个列表,来生成一个进度条。
这个进度条以9为单位
1 | >>> for i in tqdm(range(9)): |
当然除了tqdm,还有trange,使用方式完全相同
1 | >>> for i in trange(100): |
当然只要传入list就可以了
1 | >>> pbar = tqdm(["a", "b", "c", "d"]) |
手动控制更新
除了自动的更新方式,还可以手动的控制更新
1 | >>> with tqdm(total=100) as pbar: |
还可以这样
1 | >>> pbar = tqdm(total=100) |
shell的tqdm用法
官方给出的例子是这样的
1 | $ find . -name '*.py' -exec cat \{} \; | |
分析源码
仔细分析源码后发现,其实作者在文档中已经把重要的代码逻辑罗列出来了,但是却没做详尽的说明
1 | ├─tqdm |
上面是核心代码
通过看示范的代码,我们能发现使用的核心是tqdm和trange这两个函数,从代码层面分析tqdm的功能,那首先是init.py
init.py
在init.py中,首先能看到__all
1 | __all__ = ['tqdm', 'tqdm_gui', 'trange', 'tgrange', 'tqdm_pandas', |
能看到tqdm的所有功能,首先是tqdm,我们跟踪到_tqdm.py
_tqdm.py
能看到tqdm类的声明,首先是初始化
1 | def __init__(self, iterable=None, desc=None, total=None, leave=True, |
而每个参数的作用,在注释中提到了…
Parameters
iterable : iterable, optional
Iterable to decorate with a progressbar.
可迭代的进度条。
Leave blank to manually manage the updates.
留空手动管理更新??desc : str, optional
Prefix for the progressbar.
进度条的描述total : int, optional
The number of expected iterations. If unspecified,
len(iterable) is used if possible. As a last resort, only basic
progress statistics are displayed (no ETA, no progressbar).
Ifguiis True and this parameter needs subsequent updating,
specify an initial arbitrary large positive integer,
e.g. int(9e9).
预期的迭代数目,默认为None,则尽可能的迭代下去,如果gui设置为True,这里则需要后续的更新,将需要指定为一个初始随意值较大的正整数,例如int(9e9)leave : bool, optional
If [default: True], keeps all traces of the progressbar
upon termination of iteration.
保留进度条存在的痕迹,简单来说就是会把进度条的最终形态保留下来,默认为Truefile :
io.TextIOWrapperorio.StringIO, optional
Specifies where to output the progress messages
[default: sys.stderr]. Usesfile.write(str)andfile.flush()
methods.
指定消息的输出ncols : int, optional
The width of the entire output message. If specified,
dynamically resizes the progressbar to stay within this bound.
If unspecified, attempts to use environment width. The
fallback is a meter width of 10 and no limit for the counter and
statistics. If 0, will not print any meter (only stats).
整个输出消息的宽度。如果指定,动态调整的进度停留在这个边界。如果未指定,尝试使用环境的宽度。如果为0,将不打印任何东西(只统计)。mininterval : float, optional
Minimum progress update interval, in seconds [default: 0.1].
最小进度更新间隔,以秒为单位(默认值:0.1)。maxinterval : float, optional
Maximum progress update interval, in seconds [default: 10.0].
最大进度更新间隔,以秒为单位(默认值:10)。miniters : int, optional
Minimum progress update interval, in iterations.
If specified, will setminintervalto 0.
最小进度更新周期ascii : bool, optional
If unspecified or False, use unicode (smooth blocks) to fill
the meter. The fallback is to use ASCII characters1-9 #.
如果不设置,默认为unicode编码disable : bool, optional
Whether to disable the entire progressbar wrapper
[default: False].
是否禁用整个进度条包装(如果为True,进度条不显示)unit : str, optional
String that will be used to define the unit of each iteration
[default: it].
将被用来定义每个单元的字符串???unit_scale : bool, optional
If set, the number of iterations will be reduced/scaled
automatically and a metric prefix following the
International System of Units standard will be added
(kilo, mega, etc.) [default: False].
如果设置,迭代的次数会自动按照十、百、千来添加前缀,默认为falsedynamic_ncols : bool, optional
If set, constantly altersncolsto the environment (allowing
for window resizes) [default: False].
不断改变ncols环境,允许调整窗口大小smoothing : float, optional
Exponential moving average smoothing factor for speed estimates
(ignored in GUI mode). Ranges from 0 (average speed) to 1
(current/instantaneous speed) [default: 0.3].bar_format : str, optional
Specify a custom bar string formatting. May impact performance.
If unspecified, will use ‘{l_bar}{bar}{r_bar}’, where l_bar is
‘{desc}{percentage:3.0f}%|’ and r_bar is
‘| {n_fmt}/{total_fmt} [{elapsed_str}<{remaining_str}, {rate_fmt}]’
Possible vars: bar, n, n_fmt, total, total_fmt, percentage,
rate, rate_fmt, elapsed, remaining, l_bar, r_bar, desc.
自定义栏字符串格式化…默认会使用{l_bar}{bar}{r_bar}的格式,格式同上initial : int, optional
The initial counter value. Useful when restarting a progress
bar [default: 0].
初始计数器值,默认为0position : int, optional
Specify the line offset to print this bar (starting from 0)
Automatic if unspecified.
Useful to manage multiple bars at once (eg, from threads).
指定偏移,这个功能在多个条中有用gui : bool, optional
WARNING: internal parameter - do not use.
Use tqdm_gui(…) instead. If set, will attempt to use
matplotlib animations for a graphical output [default: False].
内部参数…
Returns
- out : decorated iterator.
返回为一个迭代器
其实不用分析更多代码,这里已经把tqdm的核心功能展示出来了,接下来我们看别的函数
trange
在_tqdm文件的最后我们能找到trange的定义
1 | def trange(*args, **kwargs): |
很容易看到其实就是调用了相应参数的tqdm。
所以
1 | for i in trange(10): #same as: for i in tqdm(xrange(10)) |
tqdm的write方法
仔细分析文档的发现作者在不经意间还是写了很多重要的东西,就比如tqdm的write方法。
如果测试过,你就会发现如果我们在tqdm的每次迭代中,输出任何语句,都会使得tqdm会重新输出一个新的进度条。
但是其实tqdm模块本身提供了输出信息的方法,也就是write方法
具体使用方法是这样的
1 | >>> from tqdm import tqdm, trange |
事实上,文档中作者提到一个问题,由于环境的不确定,所以直接把print替换成tqdm.write()或许是不可取的,但其实我们只要把sys.stdout重定向到tqdm.write()就可以了,因为write的输出其实是就是sys.stdout输出的。
1 | def write(cls, s, file=sys.stdout, end="\n"): |
我们可以通过实例化一个类来完成,文档中给了代码
1 | from time import sleep |
我们只需要调用with stdout_redirect_to_tqdm() as save_stdout:
就可以自动把print重定向到tqdm.write()
tqdm_notebook & tnrange
通过上面的代码来看,这两个函数也相同,tnrange可以说是tqdm_notebook的短标签。
1 | def tnrange(*args, **kwargs): |
所以
1 | > for i in tnrange(10): #same as: for i in tqdm_notebook(xrange(10)) |
但是有很多问题,文档里这部分代码是
1 | from tqdm import tnrange, tqdm_notebook |
但是我执行会报错
1 | Traceback (most recent call last): |
跟踪过去,发现是模块加载的有问题
1 | try: # IPython 4.x / 3.x |
这里报错pass了,但是后面仍然引用了IntProgress
好吧…我放弃了
tgrange & tqdm_gui
1 | > from tqdm_gui import tgrange[, tqdm_gui] |
tqdm_pandas
1 | >>> import pandas as pd |
有时间继续补完…