目前尚不清楚问题是什么(您没有显示所有计算),但我相信您的方法可以通过您的进度条处理 iterable 的方式得到改进:
- 首先,您假设
iterable 是可索引的,但情况并非总是如此。
- 如果它是一个生成器函数,那么长度可能无法使用
len 函数确定,也不会将生成器转换为列表以获得其长度必然是有效的,并且它可能会破坏具有进度条的目的,如下例所示。因此,您的界面应允许用户传递可选的total 参数(如tqdm 所做的那样)以明确指定iterable 的长度。
- 您可以在函数
new 之外进行一些前期计算,以便new 可以根据index 参数的值快速计算条形应该有多宽。
我建议进行以下更改:
def progressbar(iterable, total=None):
def new(index):
#... print the progressbar
from math import floor
nonlocal division, width
n_division = floor(index / division + .5)
remainder = width - n_division
print('|', '.' * n_division, ' ' * remainder, '|', sep='', end='\r')
if total is None:
iterable = list(iterable)
# we must convert to a list
total = len(iterable)
it = iter(iterable)
width = 60 # with of progress bar
division = total / 60 # each division represents this many completions
try:
for i in range(total):
# ensure next value exists before printing it:
yield next(it)
new(i)
except StopIteration:
pass
print()
def fun():
import time
for i in range(1000):
time.sleep(.03)
yield i
iterator = progressbar(fun(), total=1000)
values = [i for i in iterator]
print(values[0], values[-1])
多线程
将多线程作为加速处理的一种方式是有问题的。以下是这样做的(天真)尝试失败,因为尽管使用多线程从生成器函数fun 获取值,但生成器函数仍然每 0.03 秒仅生成一次值。还应该清楚的是,例如,如果iterable 是一个简单的列表,那么多线程将无法比使用单线程更快地迭代列表:
from multiprocessing.pool import ThreadPool
def progressbar(iterable, total=None):
def new(index):
#... print the progressbar
from math import floor
nonlocal division, width
n_division = floor(index / division + .5)
remainder = width - n_division
print('|', '.' * n_division, ' ' * remainder, '|', sep='', end='\r')
if total is None:
iterable = list(iterable)
# we must convert to a list
total = len(iterable)
it = iter(iterable)
width = 60 # with of progress bar
division = total / 60 # each division represents this many completions
with ThreadPool(20) as pool:
for i, result in enumerate(pool.imap(lambda x: x, iterable)):
yield result
new(i)
print()
def fun():
import time
for i in range(1000):
time.sleep(.03)
yield i
iterator = progressbar(fun(), total=1000)
values = [i for i in iterator]
print(values[0], values[-1])
如果生成器函数本身使用了多线程,将会加快处理速度。但是,当然,无法控制iterable 的创建方式:
from multiprocessing.pool import ThreadPool
def progressbar(iterable, total=None):
def new(index):
#... print the progressbar
from math import floor
nonlocal division, width
n_division = floor(index / division + .5)
remainder = width - n_division
print('|', '.' * n_division, ' ' * remainder, '|', sep='', end='\r')
if total is None:
iterable = list(iterable)
# we must convert to a list
total = len(iterable)
it = iter(iterable)
width = 60 # with of progress bar
division = total / 60 # each division represents this many completions
try:
for i in range(total):
# ensure next value exists before printing it:
yield next(it)
new(i)
except StopIteration:
pass
print()
def fun():
import time
def fun2(i):
time.sleep(.03)
return i
with ThreadPool(20) as pool:
for i in pool.imap(fun2, range(1000)):
yield i
iterator = progressbar(fun(), total=1000)
values = [i for i in iterator]
print(values[0], values[-1])