分离进度跟踪和循环逻辑

cls*_*udt 3 python oop separation-of-concerns

假设我想使用进度条打印机跟踪循环的进度ProgressMeter(如本配方中所述).

def bigIteration(collection):
    for element in collection:
        doWork(element)
Run Code Online (Sandbox Code Playgroud)

我希望能够打开和关闭进度条.出于性能原因,我还想每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)
Run Code Online (Sandbox Code Playgroud)

但是,我不满意.从"审美"的角度来看,循环的功能代码现在被"污染"了一般的进度跟踪代码.

您能想到一种清晰地分离进度跟踪代码和功能代码的方法吗?(可以有进度跟踪装饰器吗?)

R. *_*des 6

看起来这个代码将受益于null对象模式.

# 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())
Run Code Online (Sandbox Code Playgroud)

(原谅我的法语,呃,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()))
Run Code Online (Sandbox Code Playgroud)

这种方法使您的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()
Run Code Online (Sandbox Code Playgroud)