禁用输出缓冲

Python的sys.stdout解释器默认启用输出缓冲吗?

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

迄今为止的build议:

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

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

来自Magnus Lycka邮件列表上的答案 :

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

你也可以用一些其他的数据stream来代替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' 
 # reopen stdout file descriptor with write mode # and 0 as the buffer size (unbuffered) sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) 

学分:“Sebastian”,在Python邮件列表的某处。

我宁愿把我的答案在如何刷新Python打印输出? 或者在Python的打印函数中调用缓冲区时刷新缓冲区? ,但是因为他们被标记为这个副本(我不同意),所以我会在这里回答。

由于Python 3.3 print()支持关键字参数“flush”( 参见文档 ):

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

是的。

你可以通过“-u”开关在命令行上禁用它。

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

 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时,可以通过在命令行上使用-u选项来禁用它。

以下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) 

这涉及到CristóvãoD. Sousa的回答,但我还没有评论。

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

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

之后,打印将总是直接刷新输出(除了flush=False )。

注意,(a)这只是部分回答了问题,因为它不会redirect所有的输出。 但我猜print是在python中创build输出到stdout / stderr的最常见的方式,所以这2行可能涵盖了大部分的用例。

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

Python 2不提供flush参数,但可以模拟Python 3types的print函数,如https://stackoverflow.com/a/27991478/3734258中所述; 。

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

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

您可以轻松地redirect所做的所有事情:

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

或者只为特定的print语句redirect:

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

要重置标准输出,你可以这样做:

 sys.stdout = sys.__stdout__ 

变种工作没有崩溃(至less在win32;python2.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) 

你也可以用stdbuf工具运行Python:

stdbuf -oL python <script>

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

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

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

(我发表了一条评论,但是不知怎的,所以再次:)

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

  2. 与输出缓冲略有关系:
    如果你迭代input中的行

    for line in sys.stdin:

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

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

可以重写调用flushsys.stdout write方法。 build议的方法实现如下。

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

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

 stdout.write = write_flush 

该代码假定stdoutfrom sys import stdout这种方式from sys import stdout