【问题标题】:Replace console output in Python在 Python 中替换控制台输出
【发布时间】:2011-09-04 09:10:15
【问题描述】:

我想知道如何像在某些 C/C++ 程序中一样在 Python 中创建那些漂亮的控制台计数器之一。

我有一个循环在做事,当前的输出如下:

Doing thing 0
Doing thing 1
Doing thing 2
...

只有最后一行更新会更整洁;

X things done.

我在许多控制台程序中都看到了这一点,我想知道我是否/如何在 Python 中做到这一点。

【问题讨论】:

标签: python


【解决方案1】:

一个简单的解决方案是在字符串前写"\r"而不添加换行符;如果字符串永远不会变短,这就足够了......

sys.stdout.write("\rDoing thing %i" % i)
sys.stdout.flush()

稍微复杂一点的是进度条......这是我正在使用的东西:

def start_progress(title):
    global progress_x
    sys.stdout.write(title + ": [" + "-"*40 + "]" + chr(8)*41)
    sys.stdout.flush()
    progress_x = 0

def progress(x):
    global progress_x
    x = int(x * 40 // 100)
    sys.stdout.write("#" * (x - progress_x))
    sys.stdout.flush()
    progress_x = x

def end_progress():
    sys.stdout.write("#" * (40 - progress_x) + "]\n")
    sys.stdout.flush()

你调用start_progress传递操作的描述,然后progress(x),其中x是百分比,最后是end_progress()

【讨论】:

  • 如果字符串比上一个短怎么办?
  • @math2001 用空格填充。
  • 仅投票支持前 2 行代码。在某些情况下,进度条部分会变慢。无论如何谢谢@6502
  • 某些程序(resticflatpak)可以更新多行控制台输出。您是否知道如何实现这一目标?
  • @Alexey:您可以使用 ANSI 转义码来移动光标、清除屏幕部分并更改颜色...见 en.wikipedia.org/wiki/ANSI_escape_code
【解决方案2】:

更优雅的解决方案可能是:

def progress_bar(current, total, bar_length = 20):
    percent = float(current) * 100 / total
    arrow   = '-' * int(percent/100 * bar_length - 1) + '>'
    spaces  = ' ' * (bar_length - len(arrow))

    print('Progress: [%s%s] %d %%' % (arrow, spaces, percent), end='\r')

valueendvalue调用这个函数,结果应该是

Progress: [------------->      ] 69 %

注意:Python 2.x 版本here

【讨论】:

  • 您应该使用Halo 以获得更好的进度条和微调器。
【解决方案3】:

python 3 中,您可以这样做以在同一行上打印:

print('', end='\r')

对于跟踪最新更新和进度特别有用。

如果想查看循环的进度,我也会推荐 tqdm from here。它将当前迭代和总迭代打印为带有预期完成时间的进度条。超级好用又快。适用于 python2 和 python3。

【讨论】:

    【解决方案4】:

    我不久前写了这篇文章,对此非常满意。随意使用。

    它需要 indextotal 以及可选的 titlebar_length。完成后,用复选标记替换沙漏。

    ⏳ Calculating: [████░░░░░░░░░░░░░░░░░░░░░] 18.0% done

    ✅ Calculating: [█████████████████████████] 100.0% done

    我提供了一个可以运行来测试它的示例。

    import sys
    import time
    
    def print_percent_done(index, total, bar_len=50, title='Please wait'):
        '''
        index is expected to be 0 based index. 
        0 <= index < total
        '''
        percent_done = (index+1)/total*100
        percent_done = round(percent_done, 1)
    
        done = round(percent_done/(100/bar_len))
        togo = bar_len-done
    
        done_str = '█'*int(done)
        togo_str = '░'*int(togo)
    
        print(f'\t⏳{title}: [{done_str}{togo_str}] {percent_done}% done', end='\r')
    
        if round(percent_done) == 100:
            print('\t✅')
    
    
    r = 50
    for i in range(r):
        print_percent_done(i,r)
        time.sleep(.02)
    

    如果感兴趣的话,我还有一个带有响应式进度条的版本,具体取决于终端宽度,使用 shutil.get_terminal_size()

    【讨论】:

      【解决方案5】:

      如果我们查看print()函数,可以不使用sys库来完成

      print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
      

      这是我的代码:

      def update(n):
          for i in range(n):
              print("i:",i,sep='',end="\r",flush=True)
              #time.sleep(1)
      

      【讨论】:

      • 我唯一要添加的是:columns,lines = os.get_terminal_size() message = f'{i} hello!' print(f'{message:
      【解决方案6】:

      对于这些年后偶然发现的任何人(就像我一样),我稍微调整了 6502 的方法以允许进度条减少和增加。在稍微更多的情况下有用。感谢 6502 提供了一个很棒的工具!

      基本上,唯一的区别是每次调用progress(x)时都会写入整行#s和-s,并且光标总是返回到小节的开头。

      def startprogress(title):
          """Creates a progress bar 40 chars long on the console
          and moves cursor back to beginning with BS character"""
          global progress_x
          sys.stdout.write(title + ": [" + "-" * 40 + "]" + chr(8) * 41)
          sys.stdout.flush()
          progress_x = 0
      
      
      def progress(x):
          """Sets progress bar to a certain percentage x.
          Progress is given as whole percentage, i.e. 50% done
          is given by x = 50"""
          global progress_x
          x = int(x * 40 // 100)                      
          sys.stdout.write("#" * x + "-" * (40 - x) + "]" + chr(8) * 41)
          sys.stdout.flush()
          progress_x = x
      
      
      def endprogress():
          """End of progress bar;
          Write full bar, then move to next line"""
          sys.stdout.write("#" * 40 + "]\n")
          sys.stdout.flush()
      

      【讨论】:

      • 我发现,如果代码调用过于频繁,这可能会导致速度变慢,所以我猜是 YMMV
      【解决方案7】:

      另一个答案可能更好,但这就是我正在做的。首先,我创建了一个名为 progress 的函数,它打印退格字符:

      def progress(x):
          out = '%s things done' % x  # The output
          bs = '\b' * 1000            # The backspace
          print bs,
          print out,
      

      然后我在我的主函数中循环调用它,如下所示:

      def main():
          for x in range(20):
              progress(x)
          return
      

      这当然会删除整行,但你可以把它弄乱来做你想做的事。我最终使用这种方法制作了一个进度条。

      【讨论】:

      • 有效,但如果上一行的字符多于下一行,则新行末尾之后的字符仍保留上一行:“拼写检查记录 417/701 [服务更改为表面] when] uminescence] cence] shmentarianism]"
      【解决方案8】:

      如果我理解得很好(不确定)您想使用&lt;CR&gt; 而不是&lt;LR&gt; 打印?

      如果可以的话,只要控制台终端允许这样做(当输出 si 重定向到文件时它会中断)。

      from __future__ import print_function
      print("count x\r", file=sys.stdout, end=" ")
      

      【讨论】:

        【解决方案9】:

        Aravind Voggu 的示例添加了更多功能:

        def progressBar(name, value, endvalue, bar_length = 50, width = 20):
                percent = float(value) / endvalue
                arrow = '-' * int(round(percent*bar_length) - 1) + '>'
                spaces = ' ' * (bar_length - len(arrow))
                sys.stdout.write("\r{0: <{1}} : [{2}]{3}%".format(\
                                 name, width, arrow + spaces, int(round(percent*100))))
                sys.stdout.flush()
                if value == endvalue:     
                     sys.stdout.write('\n\n')
        

        现在您无需替换前一个即可生成多个进度条。

        我还添加了name 作为具有固定宽度的值。

        对于两个循环和两次使用progressBar(),结果将如下所示:

        【讨论】:

          【解决方案10】:
          from time import sleep
          
          max_val = 40
          
          for done in range(max_val):
              sleep(0.05)
          
              undone = max_val - 1 - done
              proc = (100 * done) // (max_val - 1)
              print(f"\rProgress: [{('#' * done) + ('_' * undone)}] ({proc}%)", end='\r')
          
          print("\nDone!")
          
          Progress: [###################_____________________] (47%)
          
          Progress: [########################################] (100%)
          Done!
          

          【讨论】:

            【解决方案11】:

            下面的代码将每 0.3 秒从 0 到 137 计数消息,替换之前的数字。

            到后台的符号数 = 位数。

            stream = sys.stdout
            for i in range(137):
                stream.write('\b' * (len(str(i)) + 10))
                stream.write("Message : " + str(i))
                stream.flush()
                time.sleep(0.3)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-08-15
              • 1970-01-01
              • 2018-05-26
              • 1970-01-01
              • 2017-04-02
              • 1970-01-01
              相关资源
              最近更新 更多