禁用输出缓冲

时间:2020-03-06 14:28:51  来源:igfitidea点击:

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

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

到目前为止的建议:

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

还有其他方法可以在执行过程中以编程方式在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.

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'

是的。

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

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

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

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

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

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

我们无法神奇地更改系统提供的标准输出;因为它是由操作系统提供给python程序的。

# 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邮件列表上的某处。

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

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并不是一个好主意,因为这是放置不可释放的循环的地方,我们可能需要检查这些循环。)