【问题标题】:Display progress bar for running python scripts显示运行 python 脚本的进度条
【发布时间】:2019-12-28 23:20:40
【问题描述】:

我想在运行较长的 python 程序时显示一个进度条。我在网上查了一下,找到了下面这个函数。

import sys, time

def progress(count, total, status=''):
    bar_len = 60
    filled_len = int(round(bar_len * count / float(total)))

    percents = round(100.0 * count / float(total), 1)
    bar = '=' * filled_len + '-' * (bar_len - filled_len)

    sys.stdout.write('\r[%s] %s%s ...%s\r' % (bar, percents, '%', status))
    sys.stdout.flush()  

然后,我做了以下事情:

total = 1000
i = 0
while i < total:
    progress(i, total, status='Doing very long job')
    # My long python program here   
    i += 1

当我尝试上述方法时,运行我的 python 程序需要更长的时间。以上是使用progress函数并显示进度条的正确方法吗?感谢您的投入。

【问题讨论】:

  • 你使用它的方式完全没问题,你介意分享一下你的代码在有和没有它的情况下准确运行需要多少时间。
  • 当然。没有代码,程序运行大约 1 分钟。使用代码,运行时间超过 10 分钟。

标签: python pandas time


【解决方案1】:

执行中花费的额外时间是由于频繁 (1000x) 控制台刷新。 请阅读这个问题:Why is printing to stdout so slow? Can it be sped up?

为了解决这种减速问题,我建议打印状态并刷新每个例如10 或 20 次迭代:

total = 1000
i = 0
while i < total:
    if i % 20 == 0:
        progress(i, total, status='Doing very long job')
    # My long python program here   
    i += 1

但是,最好的方法是使用现有的库,例如tqdm (https://github.com/tqdm/tqdm)。它经过了很好的优化,并提供了嵌套进度条等很酷的功能。

【讨论】:

  • 你是对的,但是 1000 次刷新不是很多;在 iTerm2 上,甚至 100,000 次刷新都可以在一秒钟内完成。
  • 我刚刚尝试过,更改为 10 或 20 次迭代似乎仍然无法正常工作。我的没有进度条码的 python 程序运行大约一分钟。使用进度条码,程序运行超过 10 分钟。由于公司限制,我无法安装 tqdm。
【解决方案2】:

如果第三方库没问题,我建议使用tqdm。此外,在询问性能时,如果您报告时间,将会很有帮助。

提高性能的一种方法是不为循环的每次迭代调用进度。 IO 很昂贵,因此不断地在屏幕上打印确实会导致程序陷入困境,尤其是在您使用刷新绕过缓冲的情况下。

【讨论】:

    【解决方案3】:

    我写了一个简单的进度条代码来显示这样的东西:

    [#############4------] 67.00%

    并且要与 FOR 或 WHILE 一起放置,但您需要知道总大小:

    class Progresso:
    def __init__(self, total, steps):
    
        assert steps < 101 and type(total) == int and type(steps) == int and total > steps
    
        self.count = 0
        self.total = total
        self.steps = steps
        self.passo = total // steps
        if total < steps *10:
            self.test = 1
        else:
            self.test = total // (steps * 10)
            print(self.prog(), end='\r')
    
    def printa(self):
        self.count += 1
        if self.count % self.test == 0:
            print(self.prog(), end='\r')
    
    
    def prog(self):
        passado = self.count // self.passo
        fut = self.steps - passado + 1
        resto = int((self.count % self.passo) / self.passo * 10)
        if resto == 0:
            centro = '-'*(fut -1)
        else:
            centro = str(resto) + '-'*(fut-2)
        return '['+'#'*passado + centro + '] ' +'{:.2f}%     '.format(self.count/self.total*100)
    
    
    def fim(self):
        self.count = self.total
        print(self.prog())
    

    你在 FOR/WHILE 之前像这样开始:

    p = Progresso(200000, 20)
    

    在 FOR/WHILE 中插入 p.printa(),在 FOR/WHILE 之后结束:p.fim()

    【讨论】:

      【解决方案4】:

      通过将先前打印的进度条保存在函数属性中(请参阅https://www.python.org/dev/peps/pep-0232/),对进度功能进行相当简单的更改将确保它仅在要打印的进度条发生更改时打印和刷新:

      import sys, time
      
      def progress(count, total, status=''):
          # make sure the saved bar is initialized
          if not hasattr(progress,'lastbar'):
              progress.lastbar = ""
          bar_len = 60
          filled_len = int(round(bar_len * count / float(total)))
      
          percents = round(100.0 * count / float(total), 1)
          bar = '=' * filled_len + '-' * (bar_len - filled_len)
          # only print the bar if it is different from the previous bar
          if bar != progress.lastbar:
              sys.stdout.write('\r[%s] %s%s ...%s\r' % (bar, percents, '%', status))
              sys.stdout.flush()  
              progress.lastbar = bar
      
      total = 1000
      i = 0
      while i < total:
          progress(i, total, status='Doing very long job')
          # My long python program here   
          i += 1
      

      上面的代码仅在条形本身发生变化时打印和刷新 - 所以 60 次而不是 1000 次 - 忽略显示的百分比和状态消息的变化 - 如果这些对你很重要,你可能想要存储和比较完整的打印线,像这样(但这会做更多的打印和更多的冲洗):

      import sys, time
      
      def progress(count, total, status=''):
          if not hasattr(progress,'lastbar'):
              progress.lastbar = ""
          bar_len = 60
          filled_len = int(round(bar_len * count / float(total)))
      
          percents = round(100.0 * count / float(total), 1)
          bar = '=' * filled_len + '-' * (bar_len - filled_len)
          fullbar = '\r[%s] %s%s ...%s\r' % (bar, percents, '%', status)
          if fullbar != progress.lastbar:
              sys.stdout.write(fullbar)
              sys.stdout.flush()  
              progress.lastbar = fullbar
      
      total = 1000
      i = 0
      while i < total:
          progress(i, total, status='Doing very long job')
          # My long python program here   
          i += 1
      

      我确信也可以使用更复杂的方法将“20 分之一”的逻辑构建到进度函数中。

      可能构建条形图也是进度功能的一个昂贵部分 - 当输出不会改变时,您可以通过将 filled_len 与之前的 filled_len 进行比较来避免这种情况,甚至不构建如果值未更改,则为 bar 的字符串,如下所示:

      import sys, time
      
      def progress(count, total, status=''):
          if not hasattr(progress,'lastlen'):
              progress.lastlen = 0
          bar_len = 60
          filled_len = int(round(bar_len * count / float(total)))
          if filled_len != progress.lastlen:
              percents = round(100.0 * count / float(total), 1)
              bar = '=' * filled_len + '-' * (bar_len - filled_len)
              fullbar = '\r[%s] %s%s ...%s\r' % (bar, percents, '%', status)
              sys.stdout.write(fullbar)
              sys.stdout.flush()  
              progress.lastlen = filled_len
      
      total = 1000
      i = 0
      while i < total:
          progress(i, total, status='Doing very long job')
          # My long python program here   
          i += 1
      

      【讨论】:

        最近更新 更多