假设我想使用进度条打印机ProgressMeter
跟踪循环的进度(如此recipe中所述)。
def bigIteration(collection):
for element in collection:
doWork(element)
我希望能够打开和关闭进度条。出于性能原因,我还想每x步更新一次。我天真的做法是
def bigIteration(collection, progressbar=True):
if progressBar:
pm = progress.ProgressMeter(total=len(collection))
pc = 0
for element in collection:
if progressBar:
pc += 1
if pc % 100 = 0:
pm.update(pc)
doWork(element)
然而,我并不满意。从“美学”的角度来看,循环的功能代码现在被“污染”了一般的进度跟踪代码。
您能想到一种干净地分离进度跟踪代码和功能代码的方法吗? (可以有进度跟踪装饰器吗?)
答案 0 :(得分:6)
看起来这段代码会受益于null object pattern。
# a progress bar that uses ProgressMeter
class RealProgressBar:
pm = Nothing
def setMaximum(self, max):
pm = progress.ProgressMeter(total=max)
pc = 0
def progress(self):
pc += 1
if pc % 100 = 0:
pm.update(pc)
# a fake progress bar that does nothing
class NoProgressBar:
def setMaximum(self, max):
pass
def progress(self):
pass
# Iterate with a given progress bar
def bigIteration(collection, progressBar=NoProgressBar()):
progressBar.setMaximum(len(collection))
for element in collection:
progressBar.progress()
doWork(element)
bigIteration(collection, RealProgressBar())
(原谅我的法语,呃,Python,这不是我的母语;)希望你能得到这个想法。)
这使您可以从循环中移动进度更新逻辑,但是仍然有一些与进度相关的调用。
如果您从集合中创建一个生成器,可以在迭代时自动跟踪进度,则可以删除此部件。
# turn a collection into one that shows progress when iterated
def withProgress(collection, progressBar=NoProgressBar()):
progressBar.setMaximum(len(collection))
for element in collection:
progressBar.progress();
yield element
# simple iteration function
def bigIteration(collection):
for element in collection:
doWork(element)
# let's iterate with progress reports
bigIteration(withProgress(collection, RealProgressBar()))
这种方法使您的bigIteration
功能保持原样,并且具有高度可组合性。例如,假设你也想要添加取消你的这个大的迭代。只需创建恰好可以取消的另一个生成器。
# highly simplified cancellation token
# probably needs synchronization
class CancellationToken:
cancelled = False
def isCancelled(self):
return cancelled
def cancel(self):
cancelled = True
# iterates a collection with cancellation support
def withCancellation(collection, cancelToken):
for element in collection:
if cancelToken.isCancelled():
break
yield element
progressCollection = withProgress(collection, RealProgressBar())
cancellableCollection = withCancellation(progressCollection, cancelToken)
bigIteration(cancellableCollection)
# meanwhile, on another thread...
cancelToken.cancel()
答案 1 :(得分:2)
您可以将bigIteration
重写为生成器函数,如下所示:
def bigIteration(collection):
for element in collection:
doWork(element)
yield element
然后,你可以在此之外做很多事情:
def mycollection = [1,2,3]
if progressBar:
pm = progress.ProgressMeter(total=len(collection))
pc = 0
for item in bigIteration(mycollection):
pc += 1
if pc % 100 = 0:
pm.update(pc)
else:
for item in bigIteration(mycollection):
pass
答案 2 :(得分:1)
我的方法就是这样:
循环代码会在每次更改时(或每当要报告时)生成进度百分比。然后,进度跟踪代码从生成器读取,直到它为空;每次阅读后更新进度条。
然而,这也有一些缺点: