动态地在一行中打印输出

367

我想输出多个语句并且在语句间不要有空行。

具体来说,假设我有以下代码:

for item in range(1,100):
    print item

结果是:

1
2
3
4
.
.
.
如何使其看起来像这样:
1 2 3 4 5 ...

更好的方法是,是否可以将单个数字打印在最后一个数字上方,这样每次只有一个数字显示在屏幕上?


22个回答

553

print item 改为:

  • 在 Python 2.7 中使用 print item,
  • 在 Python 3 中使用 print(item, end=" ")

如果您想要动态打印数据,请使用以下语法:

  • 在 Python 3 中使用 print(item, sep=' ', end='', flush=True)

1
从http://docs.python.org/reference/simple_stmts.html#print:除非print语句以逗号结尾,否则将在末尾写入一个'\n'字符。如果语句只包含关键字print,则这是唯一的操作。 - ewall
5
FYI:在Python2.7中,可以通过在文件顶部添加“from future import print_function”来使用print(item,end =“ ”)。 - Hec
3
不幸的是,使用from __future__ import print_function时无法使用flush - Timmmm

172

顺便问一下......如何使其每次刷新时都将“mi”打印在同一位置,只需更改数字即可。

通常使用终端控制码来实现此功能。这是一个非常简单的情况,你只需要一个特殊字符:U+000D 回车符,用Python(和许多其他语言)中的'\r'来表示。以下是基于你的代码的完整示例:

from sys import stdout
from time import sleep
for i in range(1,20):
    stdout.write("\r%d" % i)
    stdout.flush()
    sleep(1)
stdout.write("\n") # move the cursor to the next line

关于此事有一些可能令人惊讶的事情:

  • \r 出现在字符串开头,这样,在程序运行时,光标始终会在数字后面。这不仅是为了美观:有些终端仿真器如果你反过来做会非常混乱。
  • 如果不包含最后一行,则程序终止后,你的 shell 将在数字上方打印其提示符。
  • stdout.flush 在某些系统上是必需的,否则您将不会获得任何输出。其他系统可能不需要它,但它也没有任何坏处。

如果您发现此方法无效,则应怀疑您的终端仿真器有问题。 vttest 程序可以帮助您测试它。

您可以用 print 语句替换 stdout.write,但我更喜欢不要混合使用 print 和文件对象的直接使用。


2
通常情况下,Python的文件对象会累积数据,以便可以将其作为大块发送到操作系统。这通常是访问磁盘上的文件所需的,但它会干扰这种类型的任务。flush()强制文件立即将其拥有的任何内容发送到操作系统。我很惊讶你没有使用它也能正常工作 - 直到我添加了它,我的程序才能正常运行。 - zwol
2
@user1995839 有控制码可以隐藏光标,但如果你要这样做,我强烈建议你使用ncurses来完成。 - zwol
2
在我的 Freebsd 上使用 Python 2.7,似乎必须使用 sys.stdout.flush() - Hakim
2
请注意,在Python REPL中回车键可能无法正常工作(至少在我的Ubuntu上是这样的);您需要执行一个脚本,而不仅仅是运行REPL命令,才能使所有这些工作正常。 - Mark Amery
@MarkAmery 对的,这是因为 REPL 会干扰终端本身以提供命令历史记录和编辑。更高级的 REPL 可能甚至可以让你操纵这个过程;CPython 的不行;我不知道 IPython 是否可以。 - zwol
显示剩余6条评论

56

使用print item,使打印语句省略换行符。

在Python 3中,使用print(item, end=" ")

如果您想要每个数字以相同的位置显示,例如使用以下代码(Python 2.7):

to = 20
digits = len(str(to - 1))
delete = "\b" * (digits + 1)
for i in range(to):
    print "{0}{1:{2}}".format(delete, i, digits),

在Python 3中,情况有点更复杂;在这里您需要刷新 sys.stdout,否则它不会打印任何内容,直到循环结束后:

import sys
to = 20
digits = len(str(to - 1))
delete = "\b" * (digits)
for i in range(to):
   print("{0}{1:{2}}".format(delete, i, digits), end="")
   sys.stdout.flush()

它将数字右对齐并始终删除所有内容。我还没有测试过是在每次迭代中计算要删除的数字位数是否更快。 - Tim Pietzcker
@TimPietzcker,Python3的解决方案对我无效。 这种情况是否已更改? - Wes
@TimPietzcker 输出对我来说并没有写在同一行,它仍然会将每个语句打印在新的一行。无论是否使用\b都没有任何区别。 - Wes
你要打印到哪里?我正在使用 Windows x64 上的 Python 命令行 shell。 - Tim Pietzcker
我正在脚本中从 Linux 打印到标准输出(stdout)。 - Wes
显示剩余4条评论

22

跟其他示例一样,我采用了类似的方法,但是我没有花时间计算最后一个输出长度等等。

我只是使用ANSI代码转义来回到行的开头,然后清除整个行,在打印我的当前状态输出。

import sys

class Printer():
    """Print things to stdout on one line dynamically"""
    def __init__(self,data):
        sys.stdout.write("\r\x1b[K"+data.__str__())
        sys.stdout.flush()

要在迭代循环中使用,您只需要调用类似以下的内容:

x = 1
for f in fileList:
    ProcessFile(f)
    output = "File number %d completed." % x
    Printer(output)
    x += 1   

更多内容请点击此处


15

修改

print item

print "\033[K", item, "\r",
sys.stdout.flush()
  • "\033[K" 清空该行的剩余部分
  • \r 回到该行开头
  • flush语句确保立即显示输出,以便您获得实时输出。

1
这对于Python 2.7非常好。你能提供一下你从哪里学到了\033[K代码的参考资料吗?我想更多地了解它们。 - Klik
print 已经在内部刷新了,不需要调用它。printsys.stdout.write() 不同,后者需要刷新到屏幕上。 - Gabriel Fair

15

答案太复杂了。如果您有Python 3,只需在print语句前加上\r,并添加end='', flush=True即可:

如下所示:

import time

for i in range(10):
    print(f'\r{i} foo bar', end='', flush=True)
    time.sleep(0.5)

这会直接在原地写入 0 foo bar,然后是 1 foo bar 等等。


14

您可以在打印语句中添加尾逗号,以在每次迭代中打印空格而不是换行:

print item,

或者,如果您使用的是 Python 2.6 或更高版本,则可以使用新的 print 函数,这将允许您指定每个要打印的项都不应该在末尾添加空格(或允许您指定任何其他结尾):

from __future__ import print_function
...
print(item, end="")

最后,您可以通过从sys模块导入标准输出来直接进行写操作,它会返回一个类似文件的对象:

from sys import stdout
...
stdout.write( str(item) )

5

我在使用2.7版本时,为了向用户表明程序仍在运行中,在每次循环运行时只是打印一个“.”,下面是对应的代码:

print "\b.",

它打印"."字符,每个字符之间没有空格。这样看起来更好,而且效果很不错。\b是一个退格字符,对于那些想知道的人来说。

4
我认为一个简单的连接应该可以解决问题:
nl = []
for x in range(1,10):nl.append(str(x))
print ' '.join(nl)

4
“使用列表解析语法print' '.join(str(x) for x in range(1,10))可以使代码更易读且运行速度可能更快。” - Mu Mind
我赞同使用列表推导式。在这种情况下,它甚至可以使代码更易读。 - Austin Henley

3
为了让数字相互覆盖,你可以这样做:
for i in range(1,100):
    print "\r",i,

只要数字在第一列打印,那么它应该有效。
编辑: 这是一个版本,即使它没有打印在第一列也可以工作。
prev_digits = -1
for i in range(0,1000):
    print("%s%d" % ("\b"*(prev_digits + 1), i)),
    prev_digits = len(str(i))

需要注意的是,这段代码已在Windows环境下Python 2.5版本中进行了测试并且运行良好,可以在Windows控制台中使用。根据其他人的说法,可能需要刷新标准输出才能看到结果。但具体情况因人而异。


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