在同一行上打印而不是新的一行?

117

基本上,我想做与这个人相反的事情... 嘿嘿。

Python脚本:每次将新行打印到shell而不是更新现有行

我有一个程序告诉我它进行了多少进度。

for i in some_list:
    #do a bunch of stuff.
    print i/len(some_list)*100," percent complete"

如果some_list的长度为50,那么最后一行将被打印50次。我想打印一行并保持更新该行。我知道这可能是你今天读到的最无聊的问题。我只是无法弄清楚需要在Google中输入哪四个单词才能得到答案。

更新!我尝试了mvds的建议,似乎是正确的。 新代码:

print percent_complete,"           \r",

百分比完成只是一个字符串(第一次我进行了抽象,现在我尝试更加字面)。现在的结果是它运行程序,在程序结束之前不打印任何东西,然后仅在一行上打印“100%完成”。

没有换行符(但有逗号,mvds建议的一半),它在结束时什么也不打印。然后打印:

0 percent complete     2 percent complete     3 percent complete     4 percent complete    

等等,现在的问题是有逗号时程序只会在结束后才打印出结果。

使用回车但不带逗号时,输出的行为与两者都不用时完全相同。


你可能还想检查一下 sys.stdout.isatty(),这样当程序不在终端运行时就不会输出这些东西。 - mvds
我正在从终端运行这个...不过想法不错。我相信在某些时候我会需要它的。 - chriscauley
1
背景是,在几种语言中,\n(我们现在省略)作为隐式信号来刷新到标准输出。否则,很多人会感到困惑。顺便说一句。 - mvds
19个回答

94

这被称为回车符,或者\r

使用它:

print i/len(some_list)*100," percent complete         \r",

逗号会防止print添加新行。(空格将保持行清除先前输出的内容)

另外,别忘了以print ""结束,以获得至少一个结尾换行符!


14
请确保每次打印的数据量始终相同(或者比之前的打印要多),否则最后会有不必要的内容残留。 - Nicholas Knight
快了快了...我会更新问题并附上结果。 - chriscauley
2
@dustynachos:嘿,忘记了那个细节。请参阅Python输出缓冲问题:https://dev59.com/inVD5IYBdhLWcg3wDHDm - Nicholas Knight
1
@dustynachos:(或者在每个print调用之后使用sys.stdout.flush(),如果您不关心程序的其余部分的输出缓冲,那么这可能更好) - Nicholas Knight
2
这对我不起作用。我实际上尝试过很多次,但从未成功过。我通常在Mac上使用iterm2,但大部分时间都是通过ssh连接到Linux服务器。我从未找到一种真正可行的方法来做到这一点。 - bgenchel
显示剩余2条评论

42

对我来说有效的是 Remi 和 siruisd 的回答的组合:

from __future__ import print_function
import sys

print(str, end='\r')
sys.stdout.flush()

1
过时了.. 对于我来说,只有Python 3.8.5可以这样工作: print("some string", end='\r') - parrott
在调试运行中它可以工作,但是在没有调试器的正常运行中却不行,非常奇怪。因为它应该会刷新,但实际上没有。 - trinity420
1
只需添加end='\r'即可,谢谢! - Vaidøtas I.

41

在Python 3.3及以上版本中,你不需要使用sys.stdout.flush()print(string, end='', flush=True)即可。

因此,

print('foo', end='')
print('\rbar', end='', flush=True)

会用 'bar' 覆盖 'foo'。


6
只要打印的文本以 "\r" 结尾,它就能正常工作。 - bli
1
可以在本地终端和通过SSH完美运行。 - Dee

40

从python 3.x开始,您可以这样做:

print('bla bla', end='')

(如果要在Python 2.6或2.7中使用,可以在脚本/模块顶部添加from __future__ import print_function)

Python控制台进度条示例:

import time

# status generator
def range_with_status(total):
    """ iterate from 0 to total and show progress in console """
    n=0
    while n<total:
        done = '#'*(n+1)
        todo = '-'*(total-n-1)
        s = '<{0}>'.format(done+todo)
        if not todo:
            s+='\n'        
        if n>0:
            s = '\r'+s
        print(s, end='')
        yield n
        n+=1

# example for use of status generator
for i in range_with_status(10):
    time.sleep(0.1)

\r 似乎会添加一个新行 - fccoelho
5
这样做可以去掉换行符,但不能覆盖原有内容,我认为作者想要的是覆盖原有内容。 - bgenchel
1
@bgenchel 使用 '\r'(如代码示例中所示),它正好实现了 OP 所需的功能。 - Milo Wielondek

13

对于控制台,你可能需要:

sys.stdout.flush()

强制更新。我认为在输出时使用 , 会阻塞标准输出,从而导致更新失败。


除非我立即在print语句之后运行此命令,否则Terminator每30秒才刷新一次该行,即使我使用print("...", end='\r')。谢谢。 - Bryce Guinta

11

虽然有些晚了,但由于我没有尝试所有答案,并且在搜索过程中多次遇到此答案... 在Python 3中,这种解决方案非常优雅,我相信它完全符合作者的要求,它可以更新同一行上的单个语句。请注意,如果该行缩小而不是增大,则可能需要执行某些特殊操作(例如,在字符串末尾填充空格以使字符串长度固定)。

if __name__ == '__main__':
    for i in range(100):
        print("", end=f"\rPercentComplete: {i} %")
        time.sleep(0.2)

3
Python 3.6最简单和最清晰的选项。 - DaveR

7
截至2020年底,对于我使用的Linux控制台和Python 3.8.5,只有以下代码有效:print('some string', end='\r')。参考来源:这篇文章。

4
如果您正在使用 Spyder,行将连续打印所有以前的解决方案。避免这种情况的一种方法是使用以下代码:
for i in range(1000):
    print('\r' + str(round(i/len(df)*100,1)) + '% complete', end='')
    sys.stdout.flush()

这是我唯一可行的解决方案(Python 3.8,Windows,PyCharm)。 - z33k

3

这对我很有用,我曾经试过黑客攻击,看看是否可能,但从未在我的程序中实际使用(GUI更加美观):

import time
f = '%4i %%'
len_to_clear = len(f)+1
clear = '\x08'* len_to_clear
print 'Progress in percent:'+' '*(len_to_clear),
for i in range(123):
    print clear+f % (i*100//123),
    time.sleep(0.4)
raw_input('\nDone')

3
截至2021年,对于Python 3.9.0,在Windows 10和Pycharm中,以下解决方法对我有效。 print('\r some string ', end='', flush=True)
其中,保留了HTML标签。

适用于Mac M1。 - MikeSchem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接