禁用输出缓冲

时间:2008-09-20 09:17:20

标签: python stdout buffered

默认情况下,Python的解释器是否为sys.stdout

启用了输出缓冲

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

目前的建议:

  1. 使用-u命令行开关
  2. 在每次写入后刷新的对象中包裹sys.stdout
  3. 设置PYTHONUNBUFFERED env var
  4. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
  5. 有没有其他方法可以在执行期间以编程方式在sys / sys.stdout中设置一些全局标志?

16 个答案:

答案 0 :(得分:379)

来自Magnus Lycka answer on a mailing list

  

你可以跳过整个缓冲   使用“python -u”的python进程   (或#!/ usr / bin / env python -u等)或者   设置环境变量   PYTHONUNBUFFERED。

     

你也可以用。替换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'

答案 1 :(得分:84)

我宁愿将答案放在How to flush output of Python print?Python's print function that flushes the buffer when it's called?中,但由于它们被标记为此答案的副本(我不同意),我将在此处回答。

由于Python 3.3 print()支持关键字参数“flush”(see documentation):

print('Hello World!', flush=True)

答案 2 :(得分:72)

# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

致谢:“塞巴斯蒂安”,在Python邮件列表的某个地方。

第三方编辑

最近版本的Python 3不支持

答案 3 :(得分:46)

是的,是的。

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

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

答案 4 :(得分:13)

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并不是一个好主意,因为它是放置不合理周期的地方,你可能想检查那些。)

答案 5 :(得分:12)

以下适用于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)

答案 6 :(得分:12)

这与CristóvãoD。Sousa的答案有关,但我还没有发表评论。

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

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

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

注意,(a)这只是部分回答了问题,因为它没有重定向所有输出。但我想print是在python中创建stdout / stderr输出的最常用方法,因此这两行可能涵盖了大部分用例。

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

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

答案 7 :(得分:10)

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

答案 8 :(得分:7)

您还可以使用stdbuf实用程序

运行Python

stdbuf -oL python <script>

答案 9 :(得分:3)

您还可以使用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)

答案 10 :(得分:3)

Variant工作没有崩溃(至少在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)

答案 11 :(得分:3)

(我发表评论,但它以某种方式迷失了。所以,再次:)

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

  2. 与输出缓冲有关:
    如果使用

    迭代输入中的行

    for line in sys.stdin:
    ...

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

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

答案 12 :(得分:3)

可以使用调用write的方法覆盖sys.stdout的仅 flush方法。建议的方法实现如下。

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

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

stdout.write = write_flush

该代码假定stdoutfrom sys import stdout方式导入{。}}。

答案 13 :(得分:2)

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

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

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

答案 14 :(得分:1)

获得无缓冲输出的一种方法是使用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__

答案 15 :(得分:1)

在Python 3中,您可以使用猴子补丁打印功能,以始终发送flush = True:

_orig_print = print

def print(*args, **kwargs):
    _orig_print(*args, flush=True, **kwargs)