动态打印一行

Posted

技术标签:

【中文标题】动态打印一行【英文标题】:Print in one line dynamically 【发布时间】:2011-03-16 00:56:20 【问题描述】:

我想创建几条给出标准输出的语句,而不会在语句之间看到换行符。

具体来说,假设我有:

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

结果是:

1
2
3
4
.
.
.

如何让它看起来像:

1 2 3 4 5 ...

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

【问题讨论】:

相关帖子 - How to print without newline or space? 【参考方案1】:

print item 更改为:

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

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

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

【讨论】:

From http://docs.python.org/reference/simple_stmts.html#print: > 一个'\n' 字符写在末尾,除非print 语句以逗号结尾。如果语句仅包含关键字 print,则这是唯一的操作。 FYI: print(item, end=" ") 可以通过在文件顶部添加'from future import print_function'在Python2.7中使用。 但很遗憾,flush 不适用于 from __future__ import print_function @ewall 如果我要执行print(item, end=", "),它还会将, 添加到最后一项如何防止它将, 添加到最后一项 这仅在我将'\r' 添加到打印项目的开头后才有效【参考方案2】:

顺便说一句......如何每次刷新它以便在一个地方打印mi只需更改数字即可。

一般来说,这样做的方法是使用terminal control codes。这是一个特别简单的情况,您只需要一个特殊字符:U+000D CARRIAGE RETURN,它是用 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 程序可以帮助您测试它​​。

您可以将stdout.write 替换为print 语句,但我不希望将print 与直接使用文件对象混用。

【讨论】:

什么是flush()函数?因为没有那个功能它对我有用! 通常,Python 的文件对象会累积数据,以便它们可以将数据大块发送到操作系统。通常,这是您访问磁盘上文件所需的内容,但它会干扰此类任务。 flush() 强制文件立即将它所拥有的任何内容发送到操作系统。如果没有它,我很惊讶它对你有用 - 在我添加它之前它对我不起作用。 @user1995839 有一些控制代码可以隐藏光标,但如果你要这样做,我强烈建议你使用ncurses sys.stdout.flush() 似乎是对我的FreebsdPython 2.7 的补充 请注意,回车在 Python REPL 中不能正常工作(至少对我来说,在 Ubuntu 上);您需要执行一个脚本,而不仅仅是运行一个 REPL 命令,才能使这些工作。【参考方案3】:

使用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 "01: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("01:2".format(delete, i, digits), end="")
   sys.stdout.flush()

【讨论】:

实际上,在我看来,您的解决方案每次都会打印相同数量的退格键。因此,如果 to 为 20,它将在打印数字 1..9 时打印一个额外的退格。你不应该计算循环内的数字并将其基于 i 的值吗? 它右对齐数字并始终删除所有内容。我还没有测试这样做是否更快,或者在每次迭代中计算现在要删除多少位。 或者,基于之前的 i 值来计算位数的变化。 啊,是的,我忽略了右对齐。很好的解决方案。 听起来相当复杂。如果一年后我必须查看我的代码,我可能不会明白我在那里做了什么。 (只需阅读我在 2007 年编写的程序 - 我非常很高兴我用 Python 编写了它。)【参考方案4】:

与其他示例一样, 我使用类似的方法,但不是花时间计算最后的输出长度等,

我只是使用 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   

See more here

【讨论】:

【参考方案5】:

改变

print item

print "\033[K", item, "\r",
sys.stdout.flush()
"\033[K" 清除到行尾 \r,返回到行首 flush 语句确保它立即显示,以便您获得实时输出。

【讨论】:

这非常适合 Python 2.7。您能否提供有关您在哪里了解\033[K 代码的参考资料?我想了解更多关于他们的信息。 print 已在内部刷新。无需调用它。 printsys.stdout.write() 不同,您必须刷新到屏幕【参考方案6】:

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

print item,

或者,如果您使用的是 Python 2.6 或更高版本,您可以使用新的 print 函数,该函数允许您指定在打印的每个项目的末尾甚至都不应出现空格(或允许您指定无论你想要什么):

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

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

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

【讨论】:

【参考方案7】:

这么多复杂的答案。如果你有 python 3,只需将\r 放在打印的开头,然后添加end='', flush=True

import time

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

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

【讨论】:

谢谢,这正是我需要的print('\r' + filename + " ", end = '', flush=True) 如何使用多行打印?【参考方案8】:

我认为一个简单的连接应该可以工作:

nl = []
for x in range(1,10):nl.append(str(x))
print ' '.join(nl)

【讨论】:

理解会更好,可能更快:print ' '.join(str(x) for x in range(1, 10)). 我支持使用列表推导。在这种情况下,它甚至会更容易阅读。【参考方案9】:

我在 2.7 上使用的另一个答案,我只是打印出一个“。”每次循环运行(向用户表明事情仍在运行)是这样的:

print "\b.",

它打印“。”每个字符之间没有空格。它看起来好一点,而且效果很好。对于那些想知道的人来说,\b 是一个退格字符。

【讨论】:

【参考方案10】:

要使数字相互覆盖,您可以执行以下操作:

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 控制台中。根据其他一些人的说法,可能需要刷新标准输出才能看到结果。 YMMV。

【讨论】:

【参考方案11】:

“顺便说一句......如何每次刷新它,以便在一个地方打印mi只需更改数字。”

这真是一个棘手的话题。 zack 建议(输出控制台控制代码)是实现这一目标的一种方法。

您可以使用 (n) 诅咒,但这主要适用于 *nixes。

在 Windows(这里是有趣的部分)上很少提及(我不明白为什么),您可以使用 Python 绑定到 WinAPI(http://sourceforge.net/projects/pywin32/ 默认情况下也与 ActivePython 一起使用)——这并不难,而且效果很好。这是一个小例子:

import win32console, time

output_handle = win32console.GetStdHandle(  win32console.STD_OUTPUT_HANDLE )
info = output_handle.GetConsoleScreenBufferInfo()
pos = info["CursorPosition"]

for i in "\\|/-\\|/-":
    output_handle.WriteConsoleOutputCharacter( i, pos )
    time.sleep( 1 )

或者,如果你想使用print(语句或函数,没有区别):

import win32console, time

output_handle = win32console.GetStdHandle(  win32console.STD_OUTPUT_HANDLE )
info = output_handle.GetConsoleScreenBufferInfo()
pos = info["CursorPosition"]

for i in "\\|/-\\|/-":
    print i
    output_handle.SetConsoleCursorPosition( pos )
    time.sleep( 1 )

win32console 模块使您可以使用 Windows 控制台做更多有趣的事情...我不是 WinAPI 的忠实粉丝,但最近我意识到我对它的反感至少有一半是由编写 WinAPI 代码引起的在 C 中 - pythonic 绑定更容易使用。

当然,所有其他答案都很棒而且很 Python,但是...如果我想在 previous 行上打印怎么办?还是写多行文本,而不是清除它并再次写相同的行?我的解决方案使这成为可能。

【讨论】:

【参考方案12】:

适用于 Python 2.7

for x in range(0, 3):
    print x,

适用于 Python 3

for x in range(0, 3):
    print(x, end=" ")

【讨论】:

【参考方案13】:

甚至更简单:

import time
a = 0
while True:
    print (a, end="\r")
    a += 1
    time.sleep(0.1)

end="\r" 将从第一次打印的开头 [0:] 开始覆盖。

【讨论】:

它确实显示了一个数字,但在使用 Bash 的终端中的 Mac OS 11 上,我看到插入点在第一个数字上呈正方形。这使得它难以阅读。【参考方案14】:
In [9]: print?
Type:           builtin_function_or_method
Base Class:     <type 'builtin_function_or_method'>
String Form:    <built-in function print>
Namespace:      Python builtin
Docstring:
    print(value, ..., sep=' ', end='\n', file=sys.stdout)

Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep:  string inserted between values, default a space.
end:  string appended after the last value, default a newline.

【讨论】:

【参考方案15】:

打印语句末尾的逗号省略了新行。

for i in xrange(1,100):
  print i,

但这不会覆盖。

【讨论】:

【参考方案16】:

对于那些像我一样苦苦挣扎的人,我想出了以下似乎在 python 3.7.4 和 3.5.2 中都有效的方法。

我将范围从 100 扩大到 1,000,000,因为它运行速度非常快,您可能看不到输出。这是因为设置end='\r' 的一个副作用是最终循环迭代会清除所有输出。需要更长的数字来证明它有效。 这个结果可能并非在所有情况下都是可取的,但在我的情况下很好,并且 OP 没有指定一种或另一种方式。您可能会使用 if 语句来规避这一点,该语句评估被迭代的数组的长度等。 在我的情况下,让它工作的关键是将括号"".format() 结合起来。否则,它不起作用。

下面应该按原样工作:

#!/usr/bin/env python3

for item in range(1,1000000):
    print("".format(item), end='\r', flush=True)

【讨论】:

【参考方案17】:

最好的方法是使用\r 字符

试试下面的代码:

import time
for n in range(500):
  print(n, end='\r')
  time.sleep(0.01)
print()  # start new line so most recently printed number stays

【讨论】:

【参考方案18】:

在 Python 3 中,您可以这样做:

for item in range(1,10):
    print(item, end =" ")

输出:

1 2 3 4 5 6 7 8 9 

元组:你可以用元组做同样的事情:

tup = (1,2,3,4,5)

for n in tup:
    print(n, end = " - ")

输出:

1 - 2 - 3 - 4 - 5 - 

另一个例子:

list_of_tuples = [(1,2),('A','B'), (3,4), ('Cat', 'Dog')]
for item in list_of_tuples:
    print(item)

输出:

(1, 2)
('A', 'B')
(3, 4)
('Cat', 'Dog')

你甚至可以像这样解压你的元组:

list_of_tuples = [(1,2),('A','B'), (3,4), ('Cat', 'Dog')]

# Tuple unpacking so that you can deal with elements inside of the tuple individually
for (item1, item2) in list_of_tuples:
    print(item1, item2)   

输出:

1 2
A B
3 4
Cat Dog

另一种变体:

list_of_tuples = [(1,2),('A','B'), (3,4), ('Cat', 'Dog')]
for (item1, item2) in list_of_tuples:
    print(item1)
    print(item2)
    print('\n')

输出:

1
2


A
B


3
4


Cat
Dog

【讨论】:

我已经尝试了这里的所有示例,但在 python 3.8 中都没有工作请获得更多答案。 我用python 3.7测试过 我的意思是这些选项都不会像问题末尾建议的那样覆盖最后一个数字。【参考方案19】:
for item in range(1,100):
    if item==99:
        print(item,end='')
    else:
        print (item,end=',')

输出: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25, 26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50, 51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75, 76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99

【讨论】:

【参考方案20】:

如果只想打印数字,可以避免循环:

# python 3
import time

startnumber = 1
endnumber = 100

# solution A without a for loop
start_time = time.clock()
m = map(str, range(startnumber, endnumber + 1))
print(' '.join(m))
end_time = time.clock()
timetaken = (end_time - start_time) * 1000
print('took 0ms\n'.format(timetaken))

# solution B: with a for loop
start_time = time.clock()
for i in range(startnumber, endnumber + 1):
    print(i, end=' ')
end_time = time.clock()
timetaken = (end_time - start_time) * 1000
print('\ntook 0ms\n'.format(timetaken))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
took 21.1986929975ms

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 
took 491.466823551ms

【讨论】:

使用列表理解而不是 map 应该会更快。 不错的例子。我喜欢它,但我需要显示解析结果。首先,我计算我数据库中的项目,而不是打印剩余的数量。【参考方案21】:

注意:我指出这个解决方案,因为如果下一个打印的长度小于前一个,我见过的大多数其他解决方案都不起作用。

如果您知道要删除的内容,并且可以负担一个全局变量,您可以简单地用空格覆盖最后一行。

    在打印之前,将字符串的长度存储为“n”。 打印它,但以“\r”结尾(它返回到行首)。 下一次,在打印您的消息之前,在该行上打印 'n' 个空格。
_last_print_len = 0
def reprint(msg, finish=False):
    global _last_print_len
    
    # Ovewrites line with spaces.
    print(' '*_last_print_len, end='\r')
    
    if finish:
        end = '\n'
        # If we're finishing the line, we won't need to overwrite it in the next print.
        _last_print_len = 0
    else:
        end = '\r'
        # Store len for the next print.
        _last_print_len = len(msg)
    
    # Printing message.
    print(msg, end=end)

例子:

for i in range(10):
    reprint('Loading.')
    time.sleep(1)
    reprint('Loading..')
    time.sleep(1)
    reprint('Loading...')
    time.sleep(1)

for i in range(10):
    reprint('Loading.')
    time.sleep(1)
    reprint('Loading..')
    time.sleep(1)
    reprint('Loading...', finish=True)
    time.sleep(1)

【讨论】:

【参考方案22】:

如果你想要它作为一个字符串,你可以使用

number_string = ""
for i in range(1, 100):
  number_string += str(i)
print(number_string)

【讨论】:

这不会在迭代时动态打印。您正在将输出收集到字符串变量中并在一个语句中打印。这不是问题要问的。我可能会投反对票。

以上是关于动态打印一行的主要内容,如果未能解决你的问题,请参考以下文章

动态多维数组自行覆盖

基于中断的字符串动态显示

如果没有实现,动态实现 __str__

检测到堆损坏(动态数组)

C++中动态二维数组中的地址

RDD与DataFrame动态转换(Java)