标签归档:progress-bar

多重处理:使用tqdm显示进度条

问题:多重处理:使用tqdm显示进度条

为了使我的代码更“ Pythonic”和更快,我使用“ multiprocessing”和一个map函数向其发送a)函数和b)迭代范围。

植入的解决方案(即直接在范围tqdm.tqdm(range(0,30))上调用tqdm不适用于多重处理(如以下代码中所述)。

进度条显示为0到100%(当python读取代码时?),但是它并不表示map函数的实际进度。

如何显示进度条以指示“地图”功能在哪一步?

from multiprocessing import Pool
import tqdm
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   p = Pool(2)
   r = p.map(_foo, tqdm.tqdm(range(0, 30)))
   p.close()
   p.join()

欢迎任何帮助或建议…

To make my code more “pythonic” and faster, I use “multiprocessing” and a map function to send it a) the function and b) the range of iterations.

The implanted solution (i.e., call tqdm directly on the range tqdm.tqdm(range(0, 30)) does not work with multiprocessing (as formulated in the code below).

The progress bar is displayed from 0 to 100% (when python reads the code?) but it does not indicate the actual progress of the map function.

How to display a progress bar that indicates at which step the ‘map’ function is ?

from multiprocessing import Pool
import tqdm
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   p = Pool(2)
   r = p.map(_foo, tqdm.tqdm(range(0, 30)))
   p.close()
   p.join()

Any help or suggestions are welcome…


回答 0

使用imap代替map,它返回已处理值的迭代器。

from multiprocessing import Pool
import tqdm
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   with Pool(2) as p:
      r = list(tqdm.tqdm(p.imap(_foo, range(30)), total=30))

Use imap instead of map, which returns an iterator of processed values.

from multiprocessing import Pool
import tqdm
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   with Pool(2) as p:
      r = list(tqdm.tqdm(p.imap(_foo, range(30)), total=30))

回答 1

找到的解决方案:注意!由于进行了多处理,估计时间(每个循环的迭代次数,总时间等)可能不稳定,但是进度条可以正常工作。

注意:Pool的上下文管理器仅在Python 3.3版中可用

from multiprocessing import Pool
import time
from tqdm import *

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
    with Pool(processes=2) as p:
        max_ = 30
        with tqdm(total=max_) as pbar:
            for i, _ in enumerate(p.imap_unordered(_foo, range(0, max_))):
                pbar.update()

Solution Found : Be careful! Due to multiprocessing, estimation time (iteration per loop, total time, etc.) could be unstable, but the progress bar works perfectly.

Note: Context manager for Pool is only available from Python version 3.3

from multiprocessing import Pool
import time
from tqdm import *

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
    with Pool(processes=2) as p:
        max_ = 30
        with tqdm(total=max_) as pbar:
            for i, _ in enumerate(p.imap_unordered(_foo, range(0, max_))):
                pbar.update()

回答 2

您可以p_tqdm改用。

https://github.com/swansonk14/p_tqdm

from p_tqdm import p_map
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   r = p_map(_foo, list(range(0, 30)))

You can use p_tqdm instead.

https://github.com/swansonk14/p_tqdm

from p_tqdm import p_map
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   r = p_map(_foo, list(range(0, 30)))

回答 3

很抱歉,迟到了,但是如果您需要的是并发映射,那么最新版本(tqdm>=4.42.0)现在具有以下内置功能:

from tqdm.contrib.concurrent import process_map  # or thread_map
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   r = process_map(_foo, range(0, 30), max_workers=2)

参考:https : //tqdm.github.io/docs/contrib.concurrent/https://github.com/tqdm/tqdm/blob/master/examples/parallel_bars.py

Sorry for being late but if all you need is a concurrent map, I added this functionality in tqdm>=4.42.0:

from tqdm.contrib.concurrent import process_map  # or thread_map
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   r = process_map(_foo, range(0, 30), max_workers=2)

References: https://tqdm.github.io/docs/contrib.concurrent/ and https://github.com/tqdm/tqdm/blob/master/examples/parallel_bars.py

It supports max_workers and chunksize and you can also easily switch from process_map to thread_map.


回答 4

根据XaviMartínez的回答,我编写了该函数imap_unordered_bar。可以imap_unordered与显示处理栏的唯一区别相同的方式使用它。

from multiprocessing import Pool
import time
from tqdm import *

def imap_unordered_bar(func, args, n_processes = 2):
    p = Pool(n_processes)
    res_list = []
    with tqdm(total = len(args)) as pbar:
        for i, res in tqdm(enumerate(p.imap_unordered(func, args))):
            pbar.update()
            res_list.append(res)
    pbar.close()
    p.close()
    p.join()
    return res_list

def _foo(my_number):
    square = my_number * my_number
    time.sleep(1)
    return square 

if __name__ == '__main__':
    result = imap_unordered_bar(_foo, range(5))

based on the answer of Xavi Martínez I wrote the function imap_unordered_bar. It can be used in the same way as imap_unordered with the only difference that a processing bar is shown.

from multiprocessing import Pool
import time
from tqdm import *

def imap_unordered_bar(func, args, n_processes = 2):
    p = Pool(n_processes)
    res_list = []
    with tqdm(total = len(args)) as pbar:
        for i, res in tqdm(enumerate(p.imap_unordered(func, args))):
            pbar.update()
            res_list.append(res)
    pbar.close()
    p.close()
    p.join()
    return res_list

def _foo(my_number):
    square = my_number * my_number
    time.sleep(1)
    return square 

if __name__ == '__main__':
    result = imap_unordered_bar(_foo, range(5))

回答 5

import multiprocessing as mp
import tqdm


some_iterable = ...

def some_func():
    # your logic
    ...


if __name__ == '__main__':
    with mp.Pool(mp.cpu_count()-2) as p:
        list(tqdm.tqdm(p.imap(some_func, iterable), total=len(iterable)))
import multiprocessing as mp
import tqdm


some_iterable = ...

def some_func():
    # your logic
    ...


if __name__ == '__main__':
    with mp.Pool(mp.cpu_count()-2) as p:
        list(tqdm.tqdm(p.imap(some_func, iterable), total=len(iterable)))

回答 6

当需要从并行执行函数中获取结果时,这是我的看法。这个函数可以做一些事情(我的另一篇文章对此做了进一步解释),但是关键是有一个任务待处理队列和一个任务完成队列。当工作人员完成挂起队列中的每个任务时,他们会将结果添加到任务完成队列中。您可以使用tqdm进度条将检查包装到任务完成队列中。我没有在这里放置do_work()函数的实现,这无关紧要,因为这里的消息是监视已完成任务的队列并在每次输入结果时更新进度条。

def par_proc(job_list, num_cpus=None, verbose=False):

# Get the number of cores
if not num_cpus:
    num_cpus = psutil.cpu_count(logical=False)

print('* Parallel processing')
print('* Running on {} cores'.format(num_cpus))

# Set-up the queues for sending and receiving data to/from the workers
tasks_pending = mp.Queue()
tasks_completed = mp.Queue()

# Gather processes and results here
processes = []
results = []

# Count tasks
num_tasks = 0

# Add the tasks to the queue
for job in job_list:
    for task in job['tasks']:
        expanded_job = {}
        num_tasks = num_tasks + 1
        expanded_job.update({'func': pickle.dumps(job['func'])})
        expanded_job.update({'task': task})
        tasks_pending.put(expanded_job)

# Set the number of workers here
num_workers = min(num_cpus, num_tasks)

# We need as many sentinels as there are worker processes so that ALL processes exit when there is no more
# work left to be done.
for c in range(num_workers):
    tasks_pending.put(SENTINEL)

print('* Number of tasks: {}'.format(num_tasks))

# Set-up and start the workers
for c in range(num_workers):
    p = mp.Process(target=do_work, args=(tasks_pending, tasks_completed, verbose))
    p.name = 'worker' + str(c)
    processes.append(p)
    p.start()

# Gather the results
completed_tasks_counter = 0

with tqdm(total=num_tasks) as bar:
    while completed_tasks_counter < num_tasks:
        results.append(tasks_completed.get())
        completed_tasks_counter = completed_tasks_counter + 1
        bar.update(completed_tasks_counter)

for p in processes:
    p.join()

return results

Here is my take for when you need to get results back from your parallel executing functions. This function does a few things (there is another post of mine that explains it further) but the key point is that there is a tasks pending queue and a tasks completed queue. As workers are done with each task in the pending queue they add the results in the tasks completed queue. You can wrap the check to the tasks completed queue with the tqdm progress bar. I am not putting the implementation of the do_work() function here, it is not relevant, as the message here is to monitor the tasks completed queue and update the progress bar every time a result is in.

def par_proc(job_list, num_cpus=None, verbose=False):

# Get the number of cores
if not num_cpus:
    num_cpus = psutil.cpu_count(logical=False)

print('* Parallel processing')
print('* Running on {} cores'.format(num_cpus))

# Set-up the queues for sending and receiving data to/from the workers
tasks_pending = mp.Queue()
tasks_completed = mp.Queue()

# Gather processes and results here
processes = []
results = []

# Count tasks
num_tasks = 0

# Add the tasks to the queue
for job in job_list:
    for task in job['tasks']:
        expanded_job = {}
        num_tasks = num_tasks + 1
        expanded_job.update({'func': pickle.dumps(job['func'])})
        expanded_job.update({'task': task})
        tasks_pending.put(expanded_job)

# Set the number of workers here
num_workers = min(num_cpus, num_tasks)

# We need as many sentinels as there are worker processes so that ALL processes exit when there is no more
# work left to be done.
for c in range(num_workers):
    tasks_pending.put(SENTINEL)

print('* Number of tasks: {}'.format(num_tasks))

# Set-up and start the workers
for c in range(num_workers):
    p = mp.Process(target=do_work, args=(tasks_pending, tasks_completed, verbose))
    p.name = 'worker' + str(c)
    processes.append(p)
    p.start()

# Gather the results
completed_tasks_counter = 0

with tqdm(total=num_tasks) as bar:
    while completed_tasks_counter < num_tasks:
        results.append(tasks_completed.get())
        completed_tasks_counter = completed_tasks_counter + 1
        bar.update(completed_tasks_counter)

for p in processes:
    p.join()

return results

回答 7

这种方法简单有效。

from multiprocessing.pool import ThreadPool
import time
from tqdm import tqdm

def job():
    time.sleep(1)
    pbar.update()

pool = ThreadPool(5)
with tqdm(total=100) as pbar:
    for i in range(100):
        pool.apply_async(job)
    pool.close()
    pool.join()

This approach simple and it works.

from multiprocessing.pool import ThreadPool
import time
from tqdm import tqdm

def job():
    time.sleep(1)
    pbar.update()

pool = ThreadPool(5)
with tqdm(total=100) as pbar:
    for i in range(100):
        pool.apply_async(job)
    pool.close()
    pool.join()

输出到同一行覆盖先前的输出?

问题:输出到同一行覆盖先前的输出?

我正在写一个FTP下载器。代码的一部分是这样的:

ftp.retrbinary("RETR " + file_name, process)

我正在调用函数进程来处理回调:

def process(data):
    print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    file.write(data)

输出是这样的:

1784  KB / KB 1829 downloaded!
1788  KB / KB 1829 downloaded!
etc...   

但我希望它打印此行,下次重新打印/刷新它,因此它只会显示一次,我会看到下载进度。

如何做呢?

I am writing an FTP downloader. Part of to the code is something like this:

ftp.retrbinary("RETR " + file_name, process)

I am calling function process to handle the callback:

def process(data):
    print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    file.write(data)

and output is something like this:

1784  KB / KB 1829 downloaded!
1788  KB / KB 1829 downloaded!
etc...   

but I want it to print this line and next time reprint/refresh it so it will only show it once and I will see progress of that download.

How can it be done?


回答 0

这是Python 3.x的代码:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!', end='\r')

end=关键字是什么做的工作在这里-在默认情况下,print()在一个换行符(结束\n)字符,但可以使用不同的字符串替换。在这种情况下,用回车符结束该行,而是将光标返回到当前行的开头。因此,无需sys为此类简单用法导入模块。print()实际上有许多关键字参数可以用来大大简化代码。

要在Python 2.6+上使用相同的代码,请将以下行放在文件顶部:

from __future__ import print_function

Here’s code for Python 3.x:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!', end='\r')

The end= keyword is what does the work here — by default, print() ends in a newline (\n) character, but this can be replaced with a different string. In this case, ending the line with a carriage return instead returns the cursor to the start of the current line. Thus, there’s no need to import the sys module for this sort of simple usage. print() actually has a number of keyword arguments which can be used to greatly simplify code.

To use the same code on Python 2.6+, put the following line at the top of the file:

from __future__ import print_function

回答 1

如果您只想更改一行,请使用\r\r表示回车。它的作用仅仅是将插入号放回当前行的开头。它不会删除任何内容。同样,\b可用于向后退一个字符。(某些终端可能不支持所有这些功能)

import sys

def process(data):
    size_str = os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    sys.stdout.write('%s\r' % size_str)
    sys.stdout.flush()
    file.write(data)

If all you want to do is change a single line, use \r. \r means carriage return. It’s effect is solely to put the caret back at the start of the current line. It does not erase anything. Similarly, \b can be used to go one character backward. (some terminals may not support all those features)

import sys

def process(data):
    size_str = os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    sys.stdout.write('%s\r' % size_str)
    sys.stdout.flush()
    file.write(data)

回答 2

看一下curses模块文档curses模块HOWTO

真正的基本例子:

import time
import curses

stdscr = curses.initscr()

stdscr.addstr(0, 0, "Hello")
stdscr.refresh()

time.sleep(1)

stdscr.addstr(0, 0, "World! (with curses)")
stdscr.refresh()

Have a look at the curses module documentation and the curses module HOWTO.

Really basic example:

import time
import curses

stdscr = curses.initscr()

stdscr.addstr(0, 0, "Hello")
stdscr.refresh()

time.sleep(1)

stdscr.addstr(0, 0, "World! (with curses)")
stdscr.refresh()

回答 3

这是我的小类,可以重印文本块。它会正确清除先前的文本,因此您可以用较短的新文本覆盖旧文本而不会造成混乱。

import re, sys

class Reprinter:
    def __init__(self):
        self.text = ''

    def moveup(self, lines):
        for _ in range(lines):
            sys.stdout.write("\x1b[A")

    def reprint(self, text):
        # Clear previous text by overwritig non-spaces with spaces
        self.moveup(self.text.count("\n"))
        sys.stdout.write(re.sub(r"[^\s]", " ", self.text))

        # Print new text
        lines = min(self.text.count("\n"), text.count("\n"))
        self.moveup(lines)
        sys.stdout.write(text)
        self.text = text

reprinter = Reprinter()

reprinter.reprint("Foobar\nBazbar")
reprinter.reprint("Foo\nbar")

Here’s my little class that can reprint blocks of text. It properly clears the previous text so you can overwrite your old text with shorter new text without creating a mess.

import re, sys

class Reprinter:
    def __init__(self):
        self.text = ''

    def moveup(self, lines):
        for _ in range(lines):
            sys.stdout.write("\x1b[A")

    def reprint(self, text):
        # Clear previous text by overwritig non-spaces with spaces
        self.moveup(self.text.count("\n"))
        sys.stdout.write(re.sub(r"[^\s]", " ", self.text))

        # Print new text
        lines = min(self.text.count("\n"), text.count("\n"))
        self.moveup(lines)
        sys.stdout.write(text)
        self.text = text

reprinter = Reprinter()

reprinter.reprint("Foobar\nBazbar")
reprinter.reprint("Foo\nbar")

回答 4

我发现对于python 2.7中的一个简单的print语句,只需将逗号放在your的末尾'\r'

print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!\r',

这比其他非python 3解决方案要短,但也更难以维护。

I found that for a simple print statement in python 2.7, just put a comma at the end after your '\r'.

print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!\r',

This is shorter than other non-python 3 solutions, but also more difficult to maintain.


回答 5

您只需在字符串末尾添加“ \ r”,然后在打印功能末尾添加逗号。例如:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!\r'),

You can just add ‘\r’ at the end of the string plus a comma at the end of print function. For example:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!\r'),

回答 6

我正在使用spyder 3.3.1-Windows 7-python 3.6,尽管可能不需要刷新。基于此帖子-https: //github.com/spyder-ide/spyder/issues/3437

   #works in spyder ipython console - \r at start of string , end=""
import time
import sys
    for i in range(20):
        time.sleep(0.5)
        print(f"\rnumber{i}",end="")
        sys.stdout.flush()

I am using spyder 3.3.1 – windows 7 – python 3.6 although flush may not be needed. based on this posting – https://github.com/spyder-ide/spyder/issues/3437

   #works in spyder ipython console - \r at start of string , end=""
import time
import sys
    for i in range(20):
        time.sleep(0.5)
        print(f"\rnumber{i}",end="")
        sys.stdout.flush()

回答 7

要超越python中的前一行,您需要做的就是在打印函数中添加end =’\ r’,请测试以下示例:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)

to overwiting the previous line in python all wath you need is to add end=’\r’ to the print function, test this example:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)

Tqdm-一个用于Python和CLI的快速、可扩展的进度条

TQDM

tqdm派生自阿拉伯语单词塔卡杜姆(تقدّم)可以是“进步”的意思,在西班牙语中是“我非常爱你”的缩写(德马西亚多)

立即使您的循环显示一个智能进度表-只需用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]

开销很低–每次迭代约60 ns(80 ns,tqdm.gui),并针对性能回归进行单元测试。相比之下,久负盛名的ProgressBar具有800 ns/ITER开销

除了开销低之外,tqdm使用智能算法预测剩余时间并跳过不必要的迭代显示,这在大多数情况下可以忽略不计的开销

tqdm可在任何平台(Linux、Windows、Mac、FreeBSD、NetBSD、Solaris/SunOS)、任何控制台或GUI中运行,并且与IPython/Jupyter笔记本电脑也很友好

tqdm不需要任何依赖项(甚至不需要curses!),只有Python和一个支持carriage return \rline feed \n控制字符


Installation

Latest PyPI stable release


pip install tqdm

Latest development release on GitHub

拉入并安装预发行版devel分支机构:

pip install "git+https://github.com/tqdm/tqdm.git@devel#egg=tqdm"

Latest Conda release

conda install -c conda-forge tqdm

Latest Snapcraft release

有3个频道可供选择:

snap install tqdm  # implies --stable, i.e. latest tagged release
snap install tqdm  --candidate  # master branch
snap install tqdm  --edge  # devel branch

请注意,snap二进制文件仅供CLI使用(而不是import-可用),并自动设置bash制表符完成

Latest Docker release

docker pull tqdm/tqdm
docker run -i --rm tqdm/tqdm --help

Other

还有其他(非官方的)地方tqdm可以下载,特别是对于CLI使用:


Changelog

所有更改的列表可在GitHub的版本上获得:,在wiki,或在website

Usage

tqdm是非常多才多艺的,可以有多种用途。下面给出了三个主要的原因

Iterable-based

包装tqdm()围绕任何可迭代:

from tqdm import tqdm
from time import sleep

text = ""
for char in tqdm(["a", "b", "c", "d"]):
    sleep(0.25)
    text = text + char

trange(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)

Manual

手动控制tqdm()使用with声明:

with tqdm(total=100) as pbar:
    for i in range(10):
        sleep(0.1)
        pbar.update(10)

如果可选变量total(或使用可迭代的len()),则显示预测统计信息

with也是可选的(您可以直接将tqdm()赋给一个变量,但在这种情况下,不要忘记delclose()在结尾处:

pbar = tqdm(total=100)
for i in range(10):
    sleep(0.1)
    pbar.update(10)
pbar.close()

Module

也许最奇妙的用法就是tqdm是在脚本中还是在命令行上。简单地插入tqdm(或python -m tqdm)之间的管道将通过所有stdinstdout将进度打印到时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%s--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]

FAQ and Known Issues

最常见的问题与多行输出过多有关,而不是整齐的单行进度条

  • 一般控制台:需要回车支持(CR\r)
  • 嵌套进度条:
    • 一般控制台:需要支持将光标上移到上一行。例如,IDLEConEmuPyCharm(亦请参阅herehere,以及here)缺乏全力支持
    • Windows:另外可能需要Python模块colorama要确保嵌套条位于各自的行内,请执行以下操作
  • Unicode:
    • 报告支持Unicode的环境将具有稳定、平滑的进度条。后备方案是一个ascii-仅限栏
    • Windows控制台通常只部分支持Unicode,因此often require explicit ascii=True(亦请参阅here)。这是因为普通宽度的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
  • Hanging pipes in python2:使用时tqdm在CLI上,您可能需要使用Python 3.5+才能正确缓冲
  • No intermediate output in docker-compose:使用docker-compose run而不是docker-compose uptty: true

如果您遇到任何其他困难,请浏览并归档

Documentation

(自2016年5月19日起)

class tqdm():
  """
  Decorate an iterable object, returning an iterator which acts exactly
  like the original iterable, but prints a dynamically updating
  progressbar every time a value is requested.
  """

  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):

Parameters

  • 可迭代的:可迭代,可选
    可重复使用进度条进行装饰。保留为空可手动管理更新
  • 说明:字符串,可选
    进度栏的前缀
  • 总计:整型或浮点型,可选
    预期迭代次数。如果未指定,则在可能的情况下使用len(可迭代)。如果为FLOAT(“inf”)或作为最后手段,则仅显示基本进度统计信息(无ETA,无进度条)。如果gui为True并且此参数需要后续更新,请指定初始任意大正数,例如9e9
  • 离开吧:布尔值,可选
    如果为[default:true],则在迭代终止时保留进度条的所有痕迹。如果None,只有在以下情况下才会离开position0
  • 文件:io.TextIOWrapperio.StringIO,可选
    指定输出进度消息的位置(默认值:sys.stderr)。用途file.write(str)file.flush()方法。有关编码信息,请参见write_bytes
  • 乙二醇:整型,可选
    整个输出消息的宽度。如果指定,则动态调整进度条的大小以保持在此范围内。如果未指定,则尝试使用环境宽度。后备宽度为10米,对计数器和统计数据没有限制。如果为0,则不打印任何仪表(仅打印统计数据)
  • 最小间隔:浮动,可选
    最小进度显示更新间隔[默认值:0.1]秒
  • 最大间隔:浮动,可选
    最大进度显示更新间隔[默认值:10]秒。自动调整miniters与…相对应mininterval在长时间显示更新延迟之后。仅在以下情况下才有效dynamic_miniters或者启用了监视器线程
  • 小型矿车:整型或浮点型,可选
    最小进度显示更新间隔,以迭代为单位。如果为0且dynamic_miniters,将自动调整为等于mininterval(CPU效率更高,适用于紧密循环)。如果>0,将跳过指定迭代次数的显示。调整此选项,然后mininterval以获得非常有效的循环。如果快速迭代和慢速迭代(网络、跳过项目等)的进度都不稳定,则应该将miniters设置为1
  • ASCII:布尔值或字符串,可选
    如果未指定或为False,则使用Unicode(平滑块)填充仪表。备用方法是使用ASCII码字符“123456789#”
  • 禁用:布尔值,可选
    是否禁用整个进度栏包装[默认值:false]。如果设置为None,则在非TTY上禁用
  • 单位:字符串,可选
    将用于定义每个迭代的单位的字符串[默认值:it]
  • 单位_刻度:Bool、int或Float,可选
    如果为1或True,则迭代次数将自动减少/缩放,并将添加遵循国际单位制标准的公制前缀(千、兆等)[默认值:FALSE]。如果有任何其他非零数,将缩放totaln
  • 动态参数(_N):布尔值,可选
    如果设置,则会不断更改ncolsnrows到环境(允许调整窗口大小)[默认值:FALSE]
  • 平滑:浮动,可选
    速度估计的指数移动平均平滑系数(在GUI模式中忽略)。范围从0(平均速度)到1(当前/瞬时速度)[默认值:0.3]
  • 条形图格式(_F):字符串,可选
    指定自定义条形字符串格式。可能会影响性能。[默认值:‘{l_bar}{bar}{r_bar}’],其中l_bar=‘{desc}:{percentage:3.0f}%|’,r_bar=‘|{n_fmt}/{total_fmt}[{elapsed}<{reaving},’{rate_fmt}{postfix}]‘’可能的变量:l_bar,bar,r_bar,n,n_fmt,total,total_fmt,百分比,已用rate_noinv、rate_noinv_fmt、rate_inv、rate_inv_fmt、后缀、unit_ditor、剩余、剩余_s、eta。请注意,如果{desc}为空,则会在{desc}之后自动删除尾随的“:”
  • 首字母:整型或浮点型,可选
    初始计数器值。重新启动进度条时非常有用[默认值:0]。如果使用浮点,请考虑指定{n:.3f}或类似于bar_format,或指定unit_scale
  • 职位:整型,可选
    如果未指定,请指定自动打印此条形图的行偏移量(从0开始)。一次管理多个条形图非常有用(例如,从线程)
  • 后缀:DICT或*,可选
    指定要在条末尾显示的其他统计信息。呼叫set_postfix(**postfix)如果可能(DICT)
  • 单位_除数:浮动,可选
    [默认值:1000],除非忽略unit_scale是真的吗?
  • 写入字节(_B):布尔值,可选
    如果(默认值:无)和file未指定,则将使用Python 2写入字节。如果True还将写入字节。在所有其他情况下,将默认为Unicode
  • 锁定参数(_A):元组,可选
    已传递给refresh用于中间输出(初始化、迭代和更新)
  • n行:整型,可选
    屏幕高度。如果指定,则隐藏此边界之外的嵌套条。如果未指定,则尝试使用环境高度。退而求其次是20
  • 颜色:字符串,可选
    条形图颜色(例如“绿色”、“#00ff00”)
  • 延迟:浮动,可选
    在经过[默认值:0]秒之前不显示

Extra CLI Options

  • 神志不清:CHR,可选
    分隔字符[默认值:‘n’]。使用“0”表示NULL。注:在Windows系统上,Python将‘n’转换为‘rn’
  • buf_size:整型,可选
    字符串缓冲区大小(以字节为单位)[默认值:256]在以下情况下使用delim已指定
  • 字节数:布尔值,可选
    如果为true,将计算字节数,忽略delim和Defaultunit_scale为了真的,unit_divisor设置为1024,并且unit到“B”
  • T形三通:布尔值,可选
    如果为true,则通过stdin对两个人都是stderrstdout
  • 更新:布尔值,可选
    如果为true,则将输入视为新经过的迭代,即要传递到的数字update()请注意,这很慢(~2e5 it/s),因为每个输入都必须解码为数字
  • 更新目标(_T):布尔值,可选
    如果为true,则将输入视为已用迭代总数,即要分配给的数字self.n请注意,这很慢(~2e5 it/s),因为每个输入都必须解码为数字
  • 空:布尔值,可选
    如果为true,将丢弃输入(无标准输出)
  • 人工路径:字符串,可选
    要安装tqdm手册页的目录
  • 压缩路径:字符串,可选
    放置tqdm完成的目录
  • 日志:字符串,可选
    严重|致命|错误|警告(ING)|[默认值:‘INFO’]|DEBUG|NOTSET

Returns

  • 输出:修饰迭代器
class tqdm():
  def update(self, n=1):
      """
      Manually update the progress bar, useful for streams
      such as reading files.
      E.g.:
      >>> t = tqdm(total=filesize) # Initialise
      >>> for current_buffer in stream:
      ...    ...
      ...    t.update(len(current_buffer))
      >>> t.close()
      The last line is highly recommended, but possibly not necessary if
      ``t.update()`` will be called in such a way that ``filesize`` will be
      exactly reached and printed.

      Parameters
      ----------
      n  : int or float, optional
          Increment to add to the internal counter of iterations
          [default: 1]. If using float, consider specifying ``{n:.3f}``
          or similar in ``bar_format``, or specifying ``unit_scale``.

      Returns
      -------
      out  : bool or None
          True if a ``display()`` was triggered.
      """

  def close(self):
      """Cleanup and (if leave=False) close the progressbar."""

  def clear(self, nomove=False):
      """Clear current bar display."""

  def refresh(self):
      """
      Force refresh the display of this bar.

      Parameters
      ----------
      nolock  : bool, optional
          If ``True``, does not lock.
          If [default: ``False``]: calls ``acquire()`` on internal lock.
      lock_args  : tuple, optional
          Passed to internal lock's ``acquire()``.
          If specified, will only ``display()`` if ``acquire()`` returns ``True``.
      """

  def unpause(self):
      """Restart tqdm timer from last print time."""

  def reset(self, total=None):
      """
      Resets to 0 iterations for repeated use.

      Consider combining with ``leave=True``.

      Parameters
      ----------
      total  : int or float, optional. Total to use for the new bar.
      """

  def set_description(self, desc=None, refresh=True):
      """
      Set/modify description of the progress bar.

      Parameters
      ----------
      desc  : str, optional
      refresh  : bool, optional
          Forces refresh [default: True].
      """

  def set_postfix(self, ordered_dict=None, refresh=True, **tqdm_kwargs):
      """
      Set/modify postfix (additional stats)
      with automatic formatting based on datatype.

      Parameters
      ----------
      ordered_dict  : dict or OrderedDict, optional
      refresh  : bool, optional
          Forces refresh [default: True].
      kwargs  : dict, optional
      """

  @classmethod
  def write(cls, s, file=sys.stdout, end="\n"):
      """Print a message via tqdm (without overlap with bars)."""

  @property
  def format_dict(self):
      """Public API for read-only member access."""

  def display(self, msg=None, pos=None):
      """
      Use ``self.sp`` to display ``msg`` in the specified ``pos``.

      Consider overloading this function when inheriting to use e.g.:
      ``self.some_frontend(**self.format_dict)`` instead of ``self.sp``.

      Parameters
      ----------
      msg  : str, optional. What to display (default: ``repr(self)``).
      pos  : int, optional. Position to ``moveto``
        (default: ``abs(self.pos)``).
      """

  @classmethod
  @contextmanager
  def wrapattr(cls, stream, method, total=None, bytes=True, **tqdm_kwargs):
      """
      stream  : file-like object.
      method  : str, "read" or "write". The result of ``read()`` and
          the first argument of ``write()`` should have a ``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):
      """Registers the current `tqdm` class with `pandas`."""

def trange(*args, **tqdm_kwargs):
    """
    A shortcut for `tqdm(xrange(*args), **tqdm_kwargs)`.
    On Python3+, `range` is used instead of `xrange`.
    """

Convenience Functions

def tqdm.contrib.tenumerate(iterable, start=0, total=None,
                            tqdm_class=tqdm.auto.tqdm, **tqdm_kwargs):
    """Equivalent of `numpy.ndenumerate` or builtin `enumerate`."""

def tqdm.contrib.tzip(iter1, *iter2plus, **tqdm_kwargs):
    """Equivalent of builtin `zip`."""

def tqdm.contrib.tmap(function, *sequences, **tqdm_kwargs):
    """Equivalent of builtin `map`."""

Submodules

class tqdm.notebook.tqdm(tqdm.tqdm):
    """IPython/Jupyter Notebook widget."""

class tqdm.auto.tqdm(tqdm.tqdm):
    """Automatically chooses beween `tqdm.notebook` and `tqdm.tqdm`."""

class tqdm.asyncio.tqdm(tqdm.tqdm):
  """Asynchronous version."""
  @classmethod
  def as_completed(cls, fs, *, loop=None, timeout=None, total=None,
                   **tqdm_kwargs):
      """Wrapper for `asyncio.as_completed`."""

class tqdm.gui.tqdm(tqdm.tqdm):
    """Matplotlib GUI version."""

class tqdm.tk.tqdm(tqdm.tqdm):
    """Tkinter GUI version."""

class tqdm.rich.tqdm(tqdm.tqdm):
    """`rich.progress` version."""

class tqdm.keras.TqdmCallback(keras.callbacks.Callback):
    """Keras callback for epoch and batch progress."""

class tqdm.dask.TqdmCallback(dask.callbacks.Callback):
    """Dask callback for task progress."""

contrib

这个tqdm.contrib软件包还包含实验模块:

  • tqdm.contrib.itertools:周围有薄薄的包装纸itertools
  • tqdm.contrib.concurrent:周围有薄薄的包装纸concurrent.futures
  • tqdm.contrib.discord:发布到Discord机器人
  • tqdm.contrib.telegram:发布到Telegram机器人
  • tqdm.contrib.bells:自动启用所有可选功能
    • autopandasdiscordtelegram

Examples and Advanced Usage

Description and additional stats

自定义信息可以在上动态显示和更新tqdm带有条形图的条形图descpostfix参数:

from tqdm import tqdm, trange
from random import random, randint
from time import sleep

with trange(10) as t:
    for i in t:
        # Description will be displayed on the left
        t.set_description('GEN %i' % i)
        # Postfix will be displayed on the right,
        # formatted automatically based on argument's datatype
        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", dict(value=0)]) as t:
    for i in range(10):
        sleep(0.1)
        t.postfix[1]["value"] = i / 2
        t.update()

使用时要记住的要点{postfix[...]}bar_format字符串:

  • postfix还需要以兼容格式作为初始参数传递,并且
  • postfix将自动转换为字符串(如果它是dict-类物体。要防止此行为,请在键不是字符串的字典中插入额外的项

附加内容bar_format参数也可以通过重写format_dict,并且可以使用以下命令修改栏本身ascii

from tqdm import tqdm
class TqdmExtraFormat(tqdm):
    """Provides a `total_time` format parameter"""
    @property
    def format_dict(self):
        d = super(TqdmExtraFormat, self).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:
        break
00:00 in total: 44%|0000.     | 4/9 [00:00<00:00, 962.93it/s]

请注意,{bar}还支持格式说明符[width][type]

  • width
    • 未指定(默认):自动填充ncols
    • int >= 0:固定宽度替代ncols逻辑
    • int < 0:从自动缺省值中减去
  • type
    • a:ASCII(ascii=True覆盖)
    • u:unicode(ascii=False覆盖)
    • b:空白(ascii=" "覆盖)

这意味着可以使用以下方法创建具有右对齐文本的固定栏:bar_format="{l_bar}{bar:10}|{bar:-10b}right-justified"

Nested progress bars

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 = "#{}, est. {:<04.2}s".format(n, interval * total)
    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)

请注意,在Python3中,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 = "#{}, est. {:<04.2}s".format(n, interval * total)
    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)

Hooks and callbacks

tqdm可以轻松支持回调/挂钩和手动更新。下面是一个包含以下内容的示例urllib

“urllib.urlsearche“文档

[.]
如果存在,将调用一次钩子函数
关于网络连接的建立和每次挡路阅读后的一次
之后。将向挂钩传递三个参数;块计数
到目前为止已传输的挡路大小(以字节为单位)和文件的总大小
[.]
import urllib, os
from tqdm import tqdm
urllib = getattr(urllib, 'request', urllib)

class TqdmUpTo(tqdm):
    """Provides `update_to(n)` which uses `tqdm.update(delta_n)`."""
    def update_to(self, b=1, bsize=1, tsize=None):
        """
        b  : int, optional
            Number of blocks transferred so far [default: 1].
        bsize  : int, optional
            Size of each block (in tqdm units) [default: 1].
        tsize  : int, optional
            Total size (in tqdm units). If [default: None] remains unchanged.
        """
        if tsize is not None:
            self.total = tsize
        return self.update(b * bsize - self.n)  # also sets 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:  # all optional kwargs
    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每当迭代速度存在潜在较大差异时(例如,通过补丁连接下载文件)

包装读/写方法

要通过类似文件的对象测量吞吐量,请执行以下操作readwrite方法,使用CallbackIOWrapper

from tqdm.auto import tqdm
from tqdm.utils import CallbackIOWrapper

with tqdm(total=file_obj.size,
          unit='B', unit_scale=True, unit_divisor=1024) as t:
    fobj = CallbackIOWrapper(t.update, file_obj, "read")
    while True:
        chunk = fobj.read(chunk_size)
        if not chunk:
            break
    t.reset()
    # ... continue to use `t` for something else

或者,使用更简单的wrapattr便利函数,该函数将压缩两个urllibCallbackIOWrapper下面是示例:

import urllib, os
from tqdm import tqdm

eg_link = "https://caspersci.uk.to/matryoshka.zip"
response = getattr(urllib, 'request', urllib).urlopen(eg_link)
with tqdm.wrapattr(open(os.devnull, "wb"), "write",
                   miniters=1, desc=eg_link.split('/')[-1],
                   total=getattr(response, 'length', None)) as fout:
    for chunk in response:
        fout.write(chunk)

这个requests等价物几乎完全相同:

import requests, os
from tqdm import 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()这设置为True如果一个display()已被触发

from tqdm.auto import tqdm as std_tqdm

def external_callback(*args, **kwargs):
    ...

class TqdmExt(std_tqdm):
    def update(self, n=1):
        displayed = super(TqdmExt, self).update(n):
        if displayed:
            external_callback(**self.format_dict)
        return displayed

asyncio

请注意,break当前未被异步迭代器捕获。这意味着tqdm在这种情况下,无法自行清理:

from tqdm.asyncio import tqdm

async for i in tqdm(range(9)):
    if i == 2:
        break

取而代之的是,或者调用pbar.close()手动或使用上下文管理器语法:

from tqdm.asyncio import tqdm

with tqdm(range(9)) as pbar:
    async for i in pbar:
        if i == 2:
            break

Pandas Integration

由于大众的需求,我们增加了对pandas–这里有一个例子DataFrame.progress_applyDataFrameGroupBy.progress_apply

import pandas as pd
import numpy as np
from tqdm import tqdm

df = pd.DataFrame(np.random.randint(0, 100, (100000, 6)))

# Register `pandas.progress_apply` and `pandas.Series.map_apply` with `tqdm`
# (can use `tqdm.gui.tqdm`, `tqdm.notebook.tqdm`, optional kwargs, etc.)
tqdm.pandas(desc="my bar!")

# Now you can use `progress_apply` instead of `apply`
# and `progress_map` instead of `map`
df.progress_apply(lambda x: x**2)
# can also groupby:
# df.groupby(0).progress_apply(lambda x: x**2)

如果您对这是如何工作的(以及如何为您自己的回调修改它)感兴趣,请参阅examples文件夹或导入模块,然后运行help()

Keras Integration

一个keras也可以回调:

from tqdm.keras import TqdmCallback

...

model.fit(..., verbose=0, callbacks=[TqdmCallback()])

Dask Integration

一个dask也可以回调:

from tqdm.dask import TqdmCallback

with TqdmCallback(desc="compute"):
    ...
    arr.compute()

# or use callback globally
cb = TqdmCallback(desc="global")
cb.register()
arr.compute()

IPython/Jupyter Integration

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小部件(与IPythonv1-v4和Jupyter兼容)、完全工作的嵌套条和颜色提示(蓝色:正常、绿色:已完成、红色:错误/中断、浅蓝色:无ETA);如下所示



这个notebook版本支持总宽度的百分比或像素(例如:ncols='100%'ncols='480px')

也可以让tqdm自动选择控制台或笔记本电脑版本,方法是使用autonotebook子模块:

from tqdm.autonotebook import tqdm
tqdm.pandas()

请注意,这将发出一个TqdmExperimentalWarning如果在笔记本中运行,因为它不能区分jupyter notebookjupyter console使用auto而不是autonotebook要取消显示此警告,请执行以下操作

请注意,笔记本将在创建它的单元格中显示该条。这可能是与使用它的单元格不同的单元格。如果这也不是我们想要的,

  • 将栏的创建延迟到必须显示它的单元格,或者
  • 使用创建条形图display=False,并且在稍后的蜂窝呼叫中display(bar.container)
from tqdm.notebook import tqdm
pbar = tqdm(..., display=False)
# different cell
display(pbar.container)

这个keras回调有一个display()同样可以使用的方法:

from tqdm.keras import TqdmCallback
cbk = TqdmCallback(display=False)
# different cell
cbk.display()
model.fit(..., verbose=0, callbacks=[cbk])

另一种可能是拥有一个不断重复使用的条(靠近笔记本顶部)(使用reset()而不是close())。因此,笔记本版本(与CLI版本不同)不会自动调用close()在此基础上Exception

from tqdm.notebook import tqdm
pbar = tqdm()
# different cell
iterable = range(100)
pbar.reset(total=len(iterable))  # initialise with new `total`
for i in iterable:
    pbar.update()
pbar.refresh()  # force print final status but don't `close()`

Custom Integration

要更改默认参数(如进行dynamic_ncols=True),只需使用内置的Python魔术:

from functools import partial
from tqdm import tqdm as std_tqdm
tqdm = partial(std_tqdm, dynamic_ncols=True)

要进一步自定义,请参见tqdm可以从继承以创建自定义回调(与TqdmUpTo示例above)或用于自定义前端(例如,诸如笔记本或绘图包之类的GUI)。在后一种情况下:

  1. def __init__()要呼叫,请执行以下操作super().__init__(..., gui=True)禁用端子的步骤status_printer创作
  2. 重新定义:close()clear()display()

考虑超载display()使用,例如,使用self.frontend(**self.format_dict)而不是self.sp(repr(self))

继承的一些子模块示例:

Dynamic Monitor/Meter

您可以使用tqdm作为一个不是单调递增的韵律。这可能是因为n减少(例如CPU使用情况监视器)或total变化

递归搜索文件就是一个例子。这个total是到目前为止找到的对象数,而n是属于文件(而不是文件夹)的那些对象的数量:

from tqdm import tqdm
import os.path

def find_files_recursively(path, show_progress=True):
    files = []
    # total=1 assumes `path` is a file
    t = tqdm(total=1, unit="file", disable=not show_progress)
    if not os.path.exists(path):
        raise IOError("Cannot find:" + path)

    def append_found_file(f):
        files.append(f)
        t.update()

    def list_found_dir(path):
        """returns os.listdir(path) assuming os.path.isdir(path)"""
        listing = os.listdir(path)
        # subtract 1 since a "file" we found was actually this directory
        t.total += len(listing) - 1
        # fancy way to give info without forcing a refresh
        t.set_postfix(dir=path[-10:], refresh=False)
        t.update(0)  # may trigger a refresh
        return listing

    def recursively_search(path):
        if os.path.isdir(path):
            for f in list_found_dir(path):
                recursively_search(os.path.join(path, f))
        else:
            append_found_file(path)

    recursively_search(path)
    t.set_postfix(dir=path)
    t.close()
    return files

使用update(0)是一种方便的方式来让tqdm决定何时触发显示刷新以避免控制台垃圾邮件

Writing messages

这是一项正在进行的工作(请参见#737)

因为tqdm使用简单的打印机制显示进度条,您不应该在终端中使用print()当进度条打开时

在终端中写入消息而不与其发生任何冲突tqdm条形图显示,a.write()提供了一种方法:

from tqdm.auto import tqdm, trange
from time import sleep

bar = trange(10)
for i in bar:
    # Print using tqdm class method .write()
    sleep(0.1)
    if not (i % 3):
        tqdm.write("Done task %i" % i)
    # Can also use bar.write()

默认情况下,这将打印到标准输出sys.stdout但是您可以使用file论点。例如,这可用于将写入日志文件或类的消息重定向

Redirecting writing

如果使用可以将消息打印到控制台的库,请通过替换print()使用tqdm.write()可能不是我们想要的。在这种情况下,重定向sys.stdouttqdm.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]
    # Relay exceptions
    except Exception as exc:
        raise exc
    # Always restore sys.stdout/err if necessary
    finally:
        sys.stdout, sys.stderr = orig_out_err

def some_fun(i):
    print("Fee, fi, fo,".split()[i])

# Redirect stdout to tqdm.write() (don't forget the `as save_stdout`)
with std_out_err_redirect_tqdm() as orig_stdout:
    # tqdm needs the original stdout
    # and dynamic_ncols=True to autodetect console width
    for i in tqdm(range(3), file=orig_stdout, dynamic_ncols=True):
        sleep(.5)
        some_fun(i)

# After the `with`, printing is restored
print("Done!")

Redirecting logging

类似于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 restored

Monitoring thread, intervals and miniters

tqdm实施一些技巧来提高效率和降低管理费用

  • 避免不必要的频繁条刷新:mininterval定义每次刷新之间等待的时间。tqdm始终在后台更新,但它将仅在mininterval
  • 减少检查系统时钟/时间的调用次数
  • mininterval比起配置更直观miniters巧妙的调整系统dynamic_miniters将自动调整miniters与时间相适应的迭代次数mininterval从本质上讲,tqdm将在不实际检查时间的情况下检查是否到了打印时间。通过手动设置,仍可绕过此行为miniters

但是,请考虑快速迭代和慢速迭代相结合的情况。在几次快速迭代之后,dynamic_miniters将设置miniters变成了一个大数目。当迭代速率随后减慢时,miniters将保持较大,从而降低显示更新频率。要解决此问题,请执行以下操作:

  • maxinterval定义显示刷新之间的最长时间。并发监视线程检查过期的更新,并在必要时强制更新

监视线程不应该有明显的开销,并且默认情况下保证至少每10秒更新一次。该值可以通过设置monitor_interval任何tqdm实例(即t = tqdm.tqdm(...); t.monitor_interval = 2)。可以通过设置在应用程序范围内禁用监视线程tqdm.tqdm.monitor_interval = 0在实例化任何tqdm钢筋

Merch

你可以买到tqdm branded merch现在!

Contributions

所有源代码都托管在GitHub欢迎投稿

请参阅CONTRIBUTING有关详细信息,请参阅文件

做出重大贡献的开发人员,按SLOC(幸存的代码行,git fame-wMC --excl '\.(png|gif|jpg)$'),包括:

名字 ID号 SLOC 注意事项
卡斯珀·达·科斯塔-路易斯 casperdcl ~81% 主要维护人员
斯蒂芬·拉罗克 lrq3000 ~10% 团队成员
马丁·祖格诺尼 martinzugnoni ~3% 他说:
理查德·谢里登 richardsheridan ~1% 他说:
陈广硕 chengs ~1% 他说:
凯尔·阿尔滕多夫 altendky <1% 他说:
马修·史蒂文斯 mjstevens777 <1% 他说:
哈德琳·玛丽 hadim <1% 团队成员
伊万·伊万诺夫 obiwanus <1% 他说:
丹尼尔·潘特莱特 danielpanteleit <1% 他说:
乔纳斯·哈格 jonashaag <1% 他说:
詹姆斯·E·金三世 jeking3 <1% 他说:
诺姆·约拉夫-拉斐尔 noamraph <1% 原作者
米哈伊尔·科罗博夫 kmike <1% 团队成员

Ports to Other Languages

有关列表,请访问this wiki page

LICENCE

开源(OSI批准):

引文信息:

(自2016年5月19日起)

Rich-Rich是一个Python库,用于终端中的富文本和美观的格式设置

中文 readme·Lengua española readme·Deutsche readme·Läs på svenska·日本語 readme·한국어 readme

Rich是一个Python库,用于富有终端中的文本和美观的格式

这个Rich API使您可以轻松地向终端输出添加颜色和样式。Rich还可以呈现漂亮的表格、进度条、标记、语法突出显示的源代码、回溯等等–开箱即用

有关Rich的视频介绍,请参阅calmcode.io通过@fishnets88

看看什么people are saying about Rich

兼容性

Rich适用于Linux、OSX和Windows。真彩色/表情符号适用于新的Windows终端,经典终端仅限16色。Rich需要Python 3.6.1或更高版本

Rich与Jupyter notebooks无需额外配置

正在安装

随一起安装pip或您最喜欢的PyPI包管理器

pip install rich

运行以下命令在您的终端上测试Rich Output:

python -m rich

丰富多彩的印刷品

若要毫不费力地向应用程序添加丰富的输出,可以将rich print方法,该方法与内置Python函数具有相同的签名。试试这个:

from rich import print

print("Hello, [bold magenta]World[/bold magenta]!", ":vampire:", locals())

丰富的REPL

Rich可以安装在Python REPL中,这样任何数据结构都会非常漂亮地打印和突出显示

>>> from rich import pretty
>>> pretty.install()

使用控制台

要更好地控制富终端内容,请导入并构造Console对象

from rich.console import Console

console = Console()

Console对象有一个print方法,该方法的接口有意与构建的print功能。下面是一个使用示例:

console.print("Hello", "World!")

如您所料,这将打印出来"Hello World!"去航站楼。请注意,与建筑不同的是print函数时,Rich将对文本进行自动换行以适应终端宽度

有几种方法可以将颜色和样式添加到输出中。您可以为整个输出设置样式,方法是将style关键字参数。下面是一个示例:

console.print("Hello", "World!", style="bold red")

输出将如下所示:

对于一次设置一行文本的样式来说,这是很好的。对于更细粒度的样式,Rich呈现了一个特殊的标记,该标记在语法上类似于bbcode下面是一个示例:

console.print("Where there is a [bold cyan]Will[/bold cyan] there [u]is[/u] a [i]way[/i].")

您可以使用Console对象以最小的工作量生成复杂的输出。请参阅Console API详细信息请参阅文档

丰富考察

里奇有一个inspect可以生成有关任何Python对象(如类、实例或构建)的报告的函数

>>> my_list = ["foo", "bar"]
>>> from rich import inspect
>>> inspect(my_list, methods=True)

请参阅inspect docs有关详细信息,请参阅

丰富的图书馆

RICH包含多个建筑可渲染对象您可以使用在CLI中创建优雅的输出,并帮助您调试代码

有关详细信息,请单击以下标题:

日志

Console对象有一个log()方法,该方法具有类似于print(),而且还呈现当前时间的列以及进行调用的文件和行。默认情况下,Rich将对Python结构和REPR字符串进行语法高亮显示。如果您记录一个集合(例如,词典或列表),Rich会漂亮地打印它,以便它可以放在可用的空间中。以下是其中一些功能的示例

from rich.console import Console
console = Console()

test_data = [
    {"jsonrpc": "2.0", "method": "sum", "params": [None, 1, 2, 4, False, True], "id": "1",},
    {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
    {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": "2"},
]

def test_log():
    enabled = False
    context = {
        "foo": "bar",
    }
    movies = ["Deadpool", "Rise of the Skywalker"]
    console.log("Hello from", console, "!")
    console.log(test_data, log_locals=True)


test_log()

以上将产生以下输出:

请注意log_locals参数,该参数输出一个包含调用log方法的局部变量的表

LOG方法可用于登录到终端,用于长时间运行的应用程序(如服务器),但也是非常好的调试辅助工具

日志记录处理程序

您还可以使用内置的Handler class对来自Python日志记录模块的输出进行格式化和着色。以下是输出的示例:

表情符号

要在控制台输出中插入表情符号,请将名称放在两个冒号之间。下面是一个示例:

>>> console.print(":smiley: :vampire: :pile_of_poo: :thumbs_up: :raccoon:")
😃 🧛 💩 👍 🦝

请明智地使用此功能

表格

丰富的可以灵活地呈现tables使用Unicode方框字符。边框、样式、单元格对齐等有多种格式选项

上面的动画是用table_movie.py在Examples目录中

下面是一个更简单的表格示例:

from rich.console import Console
from rich.table import Table

console = Console()

table = Table(show_header=True, header_style="bold magenta")
table.add_column("Date", style="dim", width=12)
table.add_column("Title")
table.add_column("Production Budget", justify="right")
table.add_column("Box Office", justify="right")
table.add_row(
    "Dev 20, 2019", "Star Wars: The Rise of Skywalker", "$275,000,000", "$375,126,118"
)
table.add_row(
    "May 25, 2018",
    "[red]Solo[/red]: A Star Wars Story",
    "$275,000,000",
    "$393,151,347",
)
table.add_row(
    "Dec 15, 2017",
    "Star Wars Ep. VIII: The Last Jedi",
    "$262,000,000",
    "[bold]$1,332,539,889[/bold]",
)

console.print(table)

这将产生以下输出:

请注意,控制台标记的呈现方式与print()log()事实上,Rich可以呈现的任何内容都可能包含在标题/行中(甚至其他表)

这个Table类足够智能,可以调整列的大小以适应终端的可用宽度,并根据需要对文本进行换行。下面是相同的示例,端子比上表小:

进度条

丰富的可以呈现多个无闪烁progress用于跟踪长期运行任务的条形图

对于基本用法,将任何序列包装在track函数并迭代结果。下面是一个示例:

from rich.progress import track

for step in track(range(100)):
    do_step(step)

添加多个进度条并不难。以下是文档中的一个示例:

这些列可以配置为显示您想要的任何详细信息。内置列包括完成百分比、文件大小、文件速度和剩余时间。下面是另一个示例,显示正在进行的下载:

要亲自尝试此功能,请参见examples/downloader.py它可以在显示进度的同时同时下载多个URL

状态

对于很难计算进度的情况,可以使用status方法,该方法将显示“微调器”动画和消息。动画不会阻止您正常使用控制台。下面是一个示例:

from time import sleep
from rich.console import Console

console = Console()
tasks = [f"task {n}" for n in range(1, 11)]

with console.status("[bold green]Working on tasks...") as status:
    while tasks:
        task = tasks.pop(0)
        sleep(1)
        console.log(f"{task} complete")

这将在终端中生成以下输出

微调器动画借用自cli-spinners您可以通过指定spinner参数。运行以下命令以查看可用值:

python -m rich.spinner

上面的命令在终端中生成以下输出:

Rich可以呈现一个tree带着指引线。树是显示文件结构或任何其他分层数据的理想选择

树的标签可以是简单的文本,也可以是Rich可以呈现的任何其他内容。运行以下命令进行演示:

python -m rich.tree

这将生成以下输出:

请参阅tree.py显示任何目录的树视图的脚本示例,类似于Linuxtree命令

Rich可以整齐地呈现内容columns具有相等或最佳宽度的。下面是(MacOS/Linux)的一个非常基本的克隆ls按列显示目录列表的命令:

import os
import sys

from rich import print
from rich.columns import Columns

directory = os.listdir(sys.argv[1])
print(Columns(directory))

下面的屏幕截图是columns example它以列的形式显示从API拉取的数据:

降价

Rich可以渲染markdown并且合理地将格式转换到终端

若要呈现标记,请将Markdown类,并使用包含标记代码的字符串构造它。然后将其打印到控制台。下面是一个示例:

from rich.console import Console
from rich.markdown import Markdown

console = Console()
with open("README.md") as readme:
    markdown = Markdown(readme.read())
console.print(markdown)

这将产生类似以下内容的输出:

语法突出显示

Rich使用pygments要实施的库syntax highlighting用法类似于呈现标记;构造Syntax对象,并将其打印到控制台。下面是一个示例:

from rich.console import Console
from rich.syntax import Syntax

my_code = '''
def iter_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]:
    """Iterate and generate a tuple with a flag for first and last value."""
    iter_values = iter(values)
    try:
        previous_value = next(iter_values)
    except StopIteration:
        return
    first = True
    for value in iter_values:
        yield first, False, previous_value
        first = False
        previous_value = value
    yield first, True, previous_value
'''
syntax = Syntax(my_code, "python", theme="monokai", line_numbers=True)
console = Console()
console.print(syntax)

这将产生以下输出:

跟踪回溯

Rich可以渲染beautiful tracebacks它们比标准Python回溯更容易阅读和显示更多代码。您可以将Rich设置为默认的回溯处理程序,这样所有未捕获的异常都将由Rich呈现

下面是它在OSX上的样子(与Linux类似):

所有丰富渲染器都使用Console Protocol,您还可以使用它来实现您自己的富内容

为企业发财致富

作为Tidelift订阅的一部分提供

Rich和数千个其他软件包的维护者正在与Tidelift合作,为您用来构建应用程序的开源软件包提供商业支持和维护。节省时间、降低风险并提高代码的健全性,同时付钱给您所使用的包的维护者。Learn more.

使用Rich的项目

以下是一些使用Rich的项目: