Separation of progress tracking and loop logic

Suppose I want to track the progress of a cycle using a progress bar printer ProgressMeter(as described in this recipe ).

def bigIteration(collection):
    for element in collection:
        doWork(element)

I would like to turn on and off the progress bar. I also want to update it only every x steps for performance reasons. My naive way to do this is

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)

However, I am not satisfied. In terms of aesthetics, the functional loop code is now “dirty” with the general progress tracking code.

Can you think of a way to cleanly separate tracking code from functional code? (Could there be a progress tracking decoder or something else?)

+3
source share
3 answers

, .

# 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 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()
+6

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

:

( ). , ; .

:

  • , , , .
  • You cannot easily return a value at the end. The solution will wrap the return value, although the progress method can determine if the function caused a progress update or return value. Actually, it would be better to wrap the progress update, so the regular return value can be unpacked, but this will require much more wrapping, since this will need to be done for each update of the run, and not just once.
+1
source

All Articles