Home Articles

禁用输出缓冲

Asked
Viewed 1225 times
422

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

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

建议到目前为止:

  • 使用 -u 命令行开关

  • 在每次写入后刷新的对象中包装 sys.stdout

  • 设置 PYTHONUNBUFFERED env var

  • sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

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

16 Answers

  • 364

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

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

    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所述 .

  • 75

    来自Magnus Lycka answer on a mailing list

    您可以使用“python -u”(或#!/ usr / bin / env python -u等)或通过设置环境变量PYTHONUNBUFFERED来跳过整个python进程的缓冲 . 您还可以将sys.stdout替换为其他流,例如在每次调用后执行刷新的包装器 . class Unbuffered(object):
    def __init __(self,stream):
    self.stream = stream
    def写(自我,数据):
    self.stream.write(数据)
    self.stream.flush()
    def writelines(self,datas):
    self.stream.writelines(DATAS)
    self.stream.flush()
    def __getattr __(self,attr):
    return getattr(self.stream,attr)

    导入系统
    sys.stdout =未缓冲(sys.stdout)
    打印'你好'

  • 72

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

    在Python 3中,你可以修补打印功能,总是发送flush = True:

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

    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

    我宁愿把我的答案放在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)
    
  • 11

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

    stdbuf -oL python <script>

  • 9

    可以仅覆盖 sys.stdoutwrite 方法和一个调用 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 .

  • 6

    以下适用于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)
    
  • 3
    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并不是一个好主意,因为它是放置不合理周期的地方,你可能想要检查那些 . )

  • 3

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

  • 3

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

    • 正如我所注意到的,CPython(至少在Linux上)根据输出的位置而表现不同 . 如果它转到tty,那么在每个' \n' 之后刷新输出
      如果它进入管道/进程,则它被缓冲,您可以使用基于 flush() 的解决方案或上面推荐的 -u 选项 .

    • 与输出缓冲有关:
      如果迭代输入中的行

    for line in sys.stdin:
    ...

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

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

  • 3

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

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

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

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

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

    3rd Party EDIT

    Unsupported in recent versions of Python 3

  • 1

    是的 .

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

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

  • 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__
    

Related