关于python:禁用输出缓冲

Disable output buffering

在python的sys.stdout解释器中,默认情况下是否启用输出缓冲?

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

迄今为止的建议:

  • 使用-u命令行开关
  • 在每次写入后刷新的对象中包装sys.stdout
  • 设置PYTHONUNBUFFEREDenv var
  • 江户十一〔四〕号
  • 在执行过程中,是否有其他方法以编程方式在sys/sys.stdout中设置一些全局标志?


    从邮件列表上的Magnus Lycka回复:

    You can skip buffering for a whole
    python process using"python -u"
    (or#!/usr/bin/env python -u etc) or by
    setting the environment variable
    PYTHONUNBUFFERED.

    You could also replace sys.stdout with
    some other stream like wrapper which
    does a flush after every call.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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的print函数中,调用缓冲区时刷新缓冲区?,但是由于它们被标记为这一个的副本(我不同意),我将在这里回答。

    因为python 3.3 print()支持关键字参数"flush"(参见文档):

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


    1
    2
    3
    # 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中不受支持


    是的,是的。

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

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


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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()不是等幂的,多次调用将导致如下错误:

    1
    2
    3
    4
    5
    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

    另一种可能性是:

    1
    2
    3
    4
    5
    6
    7
    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不是一个好主意,因为它是放置不可冻结循环的地方,您可能需要检查这些循环。)


    这与Crist_v有关?O.D.Sousa的回答,但我还不能发表评论。

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

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

    之后,打印将始终直接刷新输出(除了提供flush=False)。

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

    注意(b)它只在您定义它的模块/脚本中工作。这在编写模块时是很好的,因为它不会干扰sys.stdout

    python 2没有提供flush参数,但是您可以模拟这里描述的python 3-type print函数https://stackoverflow.com/a/27991478/3734258。


    在python 2.6、2.7和3.2中可以使用以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    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选项来禁用它。


    您还可以使用stdbuf工具运行python:

    埃多克斯1〔9〕


    在不崩溃的情况下工作的变体(至少在win32;python 2.7,ipython 0.12上),然后调用(多次):

    1
    2
    3
    4
    5
    6
    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,则在每个'EDOCX1[4]之后刷新输出。
    如果它进入管道/流程,那么它是缓冲的,您可以使用基于flush()的解决方案或上面建议的-u选项。

  • 与输出缓冲略有关系:
    如果您使用

    埃多克斯1〔6〕

  • 然后,cpython中的for实现将收集输入一段时间,然后为一组输入行执行循环体。如果您的脚本将要为每个输入行编写输出,这可能看起来像输出缓冲,但实际上是批处理,因此,flush()等技术都没有帮助。有趣的是,你在皮比身上没有这种行为。为了避免这种情况,您可以使用

    埃多克斯1〔8〕


    只可以用调用flush的方法重写sys.stdoutwrite方法。建议的方法实现如下。

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

    w参数的默认值将保留原始write方法引用。在定义了write_flush之后,原来的write可能会被覆盖。

    1
    stdout.write = write_flush

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


    您还可以使用fcntl在fly中更改文件标志。

    1
    2
    3
    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)


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

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

    您不能神奇地更改系统提供的stdout;因为它是由操作系统提供给您的python程序的。


    在python 3中,可以对print函数进行monkey修补,以始终发送flush=true:

    1
    2
    3
    4
    _orig_print = print

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

    获得无缓冲输出的一种方法是使用sys.stderr而不是sys.stdout,或者简单地调用sys.stdout.flush()来显式强制执行写入。

    您可以通过执行以下操作轻松重定向打印的所有内容:

    1
    2
    import sys; sys.stdout = sys.stderr
    print"Hello World!"

    或者仅仅针对特定的print声明进行重定向:

    1
    print >>sys.stderr,"Hello World!"

    要重置stdout,只需执行以下操作:

    1
    sys.stdout = sys.__stdout__