如何强制 Python 的 print
函数将缓冲输出刷新到屏幕?
python -u <script.py>
为我工作(强制刷新,无缓冲),无需修改脚本。
在 Python 3 中,print
可以采用可选的 flush
参数:
print("Hello, World!", flush=True)
在 Python 2 中,调用 print
后,执行以下操作:
import sys
sys.stdout.flush()
默认情况下,print
打印到 sys.stdout
(有关 file objects 的更多信息,请参阅文档)。
运行 python -h
,我看到了一个命令行选项:
-u :无缓冲的二进制标准输出和标准错误;也 PYTHONUNBUFFERED=x 有关与“-u”相关的内部缓冲的详细信息,请参见手册页
-u
,因此将第一行从(类似于)#!/usr/bin/python3
更改为 #!/usr/bin/python3 -u
- 现在,当您运行脚本(例如 ./my_script.py
)时,将始终为您添加 -u
从 Python 3.3 开始,您可以强制正常的 print()
函数刷新,而无需使用 sys.stdout.flush()
;只需将“flush”关键字参数设置为 true。从 the documentation:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False) 将对象打印到流文件,以 sep 分隔,后跟 end。 sep、end 和 file(如果存在)必须作为关键字参数给出。所有非关键字参数都像 str() 一样转换为字符串并写入流,由 sep 分隔,后跟 end。 sep 和 end 都必须是字符串;它们也可以是 None,这意味着使用默认值。如果没有给出对象, print() 将只写 end。文件参数必须是一个带有 write(string) 方法的对象;如果它不存在或无,将使用 sys.stdout。输出是否缓冲通常由文件决定,但如果flush关键字参数为真,则流被强制刷新。
sys.stdout.flush()
可以避免使用flush关键字吗?我的文件中有很多打印件并且不想更改它们+我希望我的文件始终刷新并且我不想写它。总是冲洗是我想要的。将 sys.stdout.flush()
放在顶部就足够了吗? (我正在使用 python 3 及更高版本)
import sys sys.stdout.flush()
仍然适用于 python3 吗?我宁愿不必修改我的整个脚本来强制刷新。
如何刷新 Python 打印的输出?
我建议五种方法来做到这一点:
在 Python 3 中,调用 print(..., flush=True) (flush 参数在 Python 2 的 print 函数中不可用,并且 print 语句没有类似物)。
在输出文件上调用 file.flush()(我们可以包装 python 2 的 print 函数来执行此操作),例如 sys.stdout
将此应用于模块中的每个打印函数调用,并使用部分函数 print = partial(print, flush=True) 应用于模块全局。
将其应用于带有传递给解释器命令的标志 (-u) 的进程
使用 PYTHONUNBUFFERED=TRUE 将其应用于环境中的每个 python 进程(并取消设置变量以撤消此操作)。
Python 3.3+
使用 Python 3.3 或更高版本,您只需将 flush=True
作为关键字参数提供给 print
函数:
print('foo', flush=True)
Python 2(或 < 3.3)
他们没有将 flush
参数反向移植到 Python 2.7 因此,如果您使用的是 Python 2(或低于 3.3),并且想要与 2 和 3 兼容的代码,我建议您使用以下兼容性代码。 (注意 __future__
导入必须位于/非常“靠近 top of your module”):
from __future__ import print_function
import sys
if sys.version_info[:2] < (3, 3):
old_print = print
def print(*args, **kwargs):
flush = kwargs.pop('flush', False)
old_print(*args, **kwargs)
if flush:
file = kwargs.get('file', sys.stdout)
# Why might file=None? IDK, but it works for print(i, file=None)
file.flush() if file is not None else sys.stdout.flush()
上面的兼容性代码将涵盖大多数用途,但要进行更彻底的处理,see the six
module。
或者,您可以在打印后调用 file.flush()
,例如,在 Python 2 中使用 print 语句:
import sys
print 'delayed output'
sys.stdout.flush()
将一个模块中的默认值更改为 flush=True
您可以通过在模块的全局范围内使用 functools.partial 来更改打印功能的默认值:
import functools
print = functools.partial(print, flush=True)
如果您查看我们的新部分函数,至少在 Python 3 中:
>>> print = functools.partial(print, flush=True)
>>> print
functools.partial(<built-in function print>, flush=True)
我们可以看到它正常工作:
>>> print('foo')
foo
我们实际上可以覆盖新的默认值:
>>> print('foo', flush=False)
foo
再次注意,这只会更改当前全局范围,因为当前全局范围上的打印名称将遮盖内置 print
函数(或取消引用兼容性函数,如果在 Python 2 中使用该函数,则在当前全局范围内)。
如果您想在函数内部而不是在模块的全局范围内执行此操作,您应该给它一个不同的名称,例如:
def foo():
printf = functools.partial(print, flush=True)
printf('print stuff like this')
如果你在函数中声明它是全局的,你就是在模块的全局命名空间中改变它,所以你应该把它放在全局命名空间中,除非那个特定的行为正是你想要的。
更改进程的默认值
我认为这里最好的选择是使用 -u
标志来获得无缓冲的输出。
$ python -u script.py
或者
$ python -um package.module
从 docs:
强制标准输入、标准输出和标准错误完全无缓冲。在重要的系统上,还将标准输入、标准输出和标准错误置于二进制模式。请注意,file.readlines() 和文件对象(用于 sys.stdin 中的行)中有内部缓冲,不受此选项的影响。要解决这个问题,您需要在 while 1: 循环中使用 file.readline()。
更改 shell 操作环境的默认值
如果将环境变量设置为非空字符串,则可以为环境中的所有 python 进程或从环境继承的环境获取此行为:
例如,在 Linux 或 OSX 中:
$ export PYTHONUNBUFFERED=TRUE
或窗口:
C:\SET PYTHONUNBUFFERED=TRUE
来自 docs:
PYTHONUNBUFFERED 如果设置为非空字符串,则等效于指定 -u 选项。
附录
这是 Python 2.7.12 中有关 print 函数的帮助 - 请注意,有 no flush
参数:
>>> from __future__ import print_function
>>> help(print)
print(...)
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.
__future__
版本不包括 flush
,因为“flush 参数是在 Python 3.3 中添加的(在 print() 通过未来导入向后移植到 2.7 之后)”bugs.python.org/issue28458
sys.stdout.flush()
可以避免使用flush关键字吗?我的文件中有很多打印件并且不想更改它们+我希望我的文件始终刷新并且我不想写它。总是冲洗是我想要的。将 sys.stdout.flush()
放在顶部就足够了吗? (我正在使用 python 3 及更高版本)
import functools; print = functools.partial(print, flush=True)
的操作(例如,在导入之后),甚至可以在 builtins.print
处将其分配给名称 print
以实现整个流程的适用性。
import functools; print2 = functools.partial(print, flush=True); builtins.print=print2
? @亚伦霍尔
此外,如 this blog post 中所建议的,可以在无缓冲模式下重新打开 sys.stdout
:
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
之后将自动刷新每个 stdout.write
和 print
操作。
UnsupportedOperation: IOStream has no fileno.
buffering is an optional integer used to set the buffering policy. Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line buffering (only usable in text mode), and an integer > 1 to indicate the size in bytes of a fixed-size chunk buffer.
在 Python 3.x 中,print()
函数得到了扩展:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
所以,你可以这样做:
print("Visiting toilet", flush=True)
使用 -u
命令行开关有效,但有点笨拙。这意味着如果用户在没有 -u
选项的情况下调用脚本,程序可能会出现不正确的行为。我通常使用自定义 stdout
,如下所示:
class flushfile:
def __init__(self, f):
self.f = f
def write(self, x):
self.f.write(x)
self.f.flush()
import sys
sys.stdout = flushfile(sys.stdout)
... 现在,您的所有 print
调用(隐式使用 sys.stdout
)将被自动flush
编辑。
def __getattr__(self,name): return object.__getattribute__(self.f, name)
使用无缓冲文件:
f = open('xyz.log', 'a', 0)
或者
sys.stdout = open('out.log', 'a', 0)
在 Python 3 中,您可以使用默认设置为 flush = True
覆盖 print 函数
def print(*objects, sep=' ', end='\n', file=sys.stdout, flush=True):
__builtins__.print(*objects, sep=sep, end=end, file=file, flush=flush)
Dan's idea 不太好用:
#!/usr/bin/env python
class flushfile(file):
def __init__(self, f):
self.f = f
def write(self, x):
self.f.write(x)
self.f.flush()
import sys
sys.stdout = flushfile(sys.stdout)
print "foo"
结果:
Traceback (most recent call last):
File "./passpersist.py", line 12, in <module>
print "foo"
ValueError: I/O operation on closed file
我认为问题在于它继承自文件类,这实际上是没有必要的。根据 sys.stdout 的文档:
stdout 和 stderr 不必是内置文件对象:任何对象都是可以接受的,只要它有一个带有字符串参数的 write() 方法。
如此变化
class flushfile(file):
至
class flushfile(object):
让它工作得很好。
这是我的版本,它也提供了 writelines() 和 fileno():
class FlushFile(object):
def __init__(self, fd):
self.fd = fd
def write(self, x):
ret = self.fd.write(x)
self.fd.flush()
return ret
def writelines(self, lines):
ret = self.writelines(lines)
self.fd.flush()
return ret
def flush(self):
return self.fd.flush
def close(self):
return self.fd.close()
def fileno(self):
return self.fd.fileno()
file
的其他版本,我收到错误消息。没有 file
类。
我在 Python 3.4 中这样做了:
'''To write to screen in real-time'''
message = lambda x: print(x, flush=True, end="")
message('I am flushing out now...')
我首先努力理解冲洗选项是如何工作的。我想做一个“加载显示”,这是我找到的解决方案:
for i in range(100000):
print('{:s}\r'.format(''), end='', flush=True)
print('Loading index: {:d}/100000'.format(i+1), end='')
第一行刷新先前的打印,第二行打印一条新的更新消息。我不知道这里是否存在单行语法。
sys.stdout.flush()
可以避免使用flush关键字吗?我的文件中有很多打印件并且不想更改它们+我希望我的文件始终刷新并且我不想写它。总是冲洗是我想要的。将sys.stdout.flush()
放在顶部就足够了吗? (我正在使用 python 3 及更高版本)print
时都需要执行sys.stdout.flush()
(或在 Python 3 中使用print(..., flush=True)
)。检查 this answer 以了解可能适合您的其他解决方案。sys.stdout.flush()
也适用于 Python 3。python -u <script.py>
为我工作,无需修改脚本。print = functools.partial(print, flush=True)