Python 的解释器中是否默认启用输出缓冲sys.stdout
?
如果答案是肯定的,那么禁用它的所有方法是什么?
到目前为止的建议:
- 使用
-u
命令行开关 - 包裹
sys.stdout
在每次写入后刷新的对象中 - 设置
PYTHONUNBUFFERED
环境变量 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
有没有其他方法可以在执行期间以编程方式在sys
/中设置一些全局标志sys.stdout
?
您可以使用“python -u”(或#!/usr/bin/env python -u 等)或通过设置环境变量 PYTHONUNBUFFERED 来跳过整个 python 进程的缓冲。
您还可以将 sys.stdout 替换为其他一些流,例如在每次调用后执行刷新的包装器。
class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) import sys sys.stdout = Unbuffered(sys.stdout) print 'Hello'
我宁愿把我的答案放在如何刷新打印功能的输出?或者在Python 的 print 函数中,当它被调用时会刷新缓冲区?,但由于它们被标记为与这个重复(我不同意),我会在这里回答。
从 Python 3.3 开始, print() 支持关键字参数“flush”(参见文档):
print('Hello World!', flush=True)
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
import io, os, sys
try:
# Python 3, open as binary, then wrap in a TextIOWrapper with write-through.
sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0), write_through=True)
# If flushing on newlines is sufficient, as of 3.7 you can instead just call:
# sys.stdout.reconfigure(line_buffering=True)
except TypeError:
# Python 2
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
致谢:“Sebastian”,位于 Python 邮件列表中的某个位置。
是的。
您可以使用“-u”开关在命令行上禁用它。
或者,您可以在每次写入时在 sys.stdout 上调用 .flush() (或用自动执行此操作的对象包装它)
这与 Cristóvão D. Sousa 的回答有关,但我还不能发表评论。
使用Python 3flush
的关键字参数以始终具有无缓冲输出的直接方法是:
import functools
print = functools.partial(print, flush=True)
之后, print 将始终直接刷新输出(flush=False
给出的除外)。
请注意,(a)这仅部分回答了问题,因为它不会重定向所有输出。但我想这是在 python 中创建输出到/print
的最常见方式,所以这两行可能涵盖了大多数用例。stdout
stderr
注意 (b) 它仅适用于您定义它的模块/脚本。这在编写模块时会很好,因为它不会与sys.stdout
.
Python 2不提供flush
参数,但您可以模拟 Python 3 类型的print
函数,如此处所述https://stackoverflow.com/a/27991478/3734258。
def disable_stdout_buffering():
# Appending to gc.garbage is a way to stop an object from being
# destroyed. If the old sys.stdout is ever collected, it will
# close() stdout, which is not good.
gc.garbage.append(sys.stdout)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# Then this will give output in the correct order:
disable_stdout_buffering()
print "hello"
subprocess.call(["echo", "bye"])
如果不保存旧的 sys.stdout,则 disable_stdout_buffering() 不是幂等的,多次调用会导致如下错误:
Traceback (most recent call last):
File "test/buffering.py", line 17, in <module>
print "hello"
IOError: [Errno 9] Bad file descriptor
close failed: [Errno 9] Bad file descriptor
另一种可能是:
def disable_stdout_buffering():
fileno = sys.stdout.fileno()
temp_fd = os.dup(fileno)
sys.stdout.close()
os.dup2(temp_fd, fileno)
os.close(temp_fd)
sys.stdout = os.fdopen(fileno, "w", 0)
(附加到 gc.garbage 并不是一个好主意,因为它是放置不可释放循环的地方,您可能需要检查这些。)
以下适用于 Python 2.6、2.7 和 3.2:
import os
import sys
buf_arg = 0
if sys.version_info[0] == 3:
os.environ['PYTHONUNBUFFERED'] = '1'
buf_arg = 1
sys.stdout = os.fdopen(sys.stdout.fileno(), 'a+', buf_arg)
sys.stderr = os.fdopen(sys.stderr.fileno(), 'a+', buf_arg)
是的,它默认启用。您可以在调用 python 时使用命令行上的 -u 选项禁用它。
在 Python 3 中,您可以对 print 函数进行猴子修补,以始终发送 flush=True:
_orig_print = print
def print(*args, **kwargs):
_orig_print(*args, flush=True, **kwargs)
正如评论中所指出的,您可以通过将 flush 参数绑定到一个值来简化这一点,方法是functools.partial
:
print = functools.partial(print, flush=True)
您还可以使用stdbuf实用程序运行 Python:
stdbuf -oL python <script>
您还可以使用 fcntl 即时更改文件标志。
fl = fcntl.fcntl(fd.fileno(), fcntl.F_GETFL)
fl |= os.O_SYNC # or os.O_DSYNC (if you don't care the file timestamp updates)
fcntl.fcntl(fd.fileno(), fcntl.F_SETFL, fl)
可以只 用调用write
的方法覆盖. 建议的方法实现如下。sys.stdout
flush
def write_flush(args, w=stdout.write):
w(args)
stdout.flush()
参数的默认值w
将保留原始write
方法引用。定义后 ,原始的可能会被覆盖。write_flush
write
stdout.write = write_flush
代码假定以stdout
这种方式导入from sys import stdout
。
获得无缓冲输出的一种方法是使用sys.stderr
而不是sys.stdout
或简单地调用sys.stdout.flush()
以显式强制写入发生。
您可以通过执行以下操作轻松重定向打印的所有内容:
import sys; sys.stdout = sys.stderr
print "Hello World!"
或仅针对特定print
语句重定向:
print >>sys.stderr, "Hello World!"
要重置标准输出,您可以这样做:
sys.stdout = sys.__stdout__
您可以创建一个无缓冲文件并将此文件分配给 sys.stdout。
import sys
myFile= open( "a.log", "w", 0 )
sys.stdout= myFile
你不能神奇地改变系统提供的标准输出;因为它是由操作系统提供给你的 python 程序的。
可以在不崩溃的情况下工作的变体(至少在 win32;python 2.7,ipython 0.12 上)然后随后调用(多次):
def DisOutBuffering():
if sys.stdout.name == '<stdout>':
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
if sys.stderr.name == '<stderr>':
sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
(我发表了评论,但不知何故丢失了。所以,再次:)
正如我所注意到的,CPython(至少在 Linux 上)的行为取决于输出的位置。如果它进入一个 tty,那么输出会在每个 '\n'
如果它进入一个管道/进程之后被刷新,那么它会被缓冲,你可以使用flush()
基于解决方案或上面推荐的-u选项。
与输出缓冲稍微相关:
如果您使用
for line in sys.stdin:
...
然后CPython中的for实现将收集输入一段时间,然后为一堆输入行执行循环体。如果您的脚本要为每个输入行写入输出,这可能看起来像输出缓冲,但实际上是批处理,因此,没有任何, etc. 技术对此有帮助。有趣的是,您在pypy中没有这种行为。为避免这种情况,您可以使用flush()
while True:
line=sys.stdin.readline()
...