609

Python 的解释器中是否默认启用输出缓冲sys.stdout

如果答案是肯定的,那么禁用它的所有方法是什么?

到目前为止的建议:

  1. 使用-u命令行开关
  2. 包裹sys.stdout在每次写入后刷新的对象中
  3. 设置PYTHONUNBUFFERED环境变量
  4. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

有没有其他方法可以在执行期间以编程方式在sys/中设置一些全局标志sys.stdout

4

16 回答 16

487

来自Magnus Lycka 在邮件列表中的回答

您可以使用“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'
于 2008-09-20T09:24:31.307 回答
179

我宁愿把我的答案放在如何刷新打印功能的输出?或者在Python 的 print 函数中,当它被调用时会刷新缓冲区?,但由于它们被标记为与这个重复(我不同意),我会在这里回答。

从 Python 3.3 开始, print() 支持关键字参数“flush”(参见文档):

print('Hello World!', flush=True)
于 2013-02-06T13:05:23.863 回答
86
# 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 邮件列表中的某个位置。

于 2008-10-08T07:23:10.007 回答
67

是的。

您可以使用“-u”开关在命令行上禁用它。

或者,您可以在每次写入时在 sys.stdout 上调用 .flush() (或用自动执行此操作的对象包装它)

于 2008-09-20T09:25:36.763 回答
33

这与 Cristóvão D. Sousa 的回答有关,但我还不能发表评论。

使用Python 3flush的关键字参数以始终具有无缓冲输出的直接方法是:

import functools
print = functools.partial(print, flush=True)

之后, print 将始终直接刷新输出(flush=False给出的除外)。

请注意,(a)这仅部分回答了问题,因为它不会重定向所有输出。但我想这是在 python 中创建输出到/print的最常见方式,所以这两行可能涵盖了大多数用例。stdoutstderr

注意 (b) 它仅适用于您定义它的模块/脚本。这在编写模块时会很好,因为它不会与sys.stdout.

Python 2不提供flush参数,但您可以模拟 Python 3 类型的print函数,如此处所述https://stackoverflow.com/a/27991478/3734258

于 2016-10-20T18:30:43.340 回答
15
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 并不是一个好主意,因为它是放置不可释放循环的地方,您可能需要检查这些。)

于 2010-09-09T15:37:53.240 回答
13

以下适用于 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)
于 2014-04-12T18:43:28.173 回答
12

是的,它默认启用。您可以在调用 python 时使用命令行上的 -u 选项禁用它。

于 2008-09-20T09:26:19.353 回答
10

在 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)
于 2018-11-26T20:07:16.463 回答
8

您还可以使用stdbuf实用程序运行 Python:

stdbuf -oL python <script>

于 2015-07-01T20:09:53.547 回答
5

您还可以使用 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)
于 2009-11-15T00:01:01.810 回答
4

可以 用调用write的方法覆盖. 建议的方法实现如下。sys.stdoutflush

def write_flush(args, w=stdout.write):
    w(args)
    stdout.flush()

参数的默认值w将保留原始write方法引用。定义 ,原始的可能会被覆盖。write_flushwrite

stdout.write = write_flush

代码假定以stdout这种方式导入from sys import stdout

于 2017-02-25T21:00:30.460 回答
3

获得无缓冲输出的一种方法是使用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__
于 2008-09-20T09:40:13.047 回答
3

您可以创建一个无缓冲文件并将此文件分配给 sys.stdout。

import sys 
myFile= open( "a.log", "w", 0 ) 
sys.stdout= myFile

你不能神奇地改变系统提供的标准输出;因为它是由操作系统提供给你的 python 程序的。

于 2008-09-20T10:39:09.753 回答
3

可以在不崩溃的情况下工作的变体(至少在 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)
于 2012-06-30T19:20:08.913 回答
3

(我发表了评论,但不知何故丢失了。所以,再次:)

  1. 正如我所注意到的,CPython(至少在 Linux 上)的行为取决于输出的位置。如果它进入一个 tty,那么输出会在每个 '\n'
    如果它进入一个管道/进程之后被刷新,那么它会被缓冲,你可以使用flush()基于解决方案或上面推荐的-u选项。

  2. 与输出缓冲稍微相关:
    如果您使用

    for line in sys.stdin:
    ...

然后CPython中的for实现将收集输入一段时间,然后为一堆输入行执行循环体。如果您的脚本要为每个输入行写入输出,这可能看起来像输出缓冲,但实际上是批处理,因此,没有任何, etc. 技术对此有帮助。有趣的是,您在pypy中没有这种行为。为避免这种情况,您可以使用flush()

while True: line=sys.stdin.readline()
...

于 2013-06-11T14:47:38.087 回答