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

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

到目前为止的建议:


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

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

评论

对于Python 3中的“打印”,请参见此答案。

我认为-u的缺点是它不适用于已编译的字节码或以__main__.py文件作为入口点的应用程序。

完整的CPython初始化逻辑在这里:github.com/python/cpython/blob/v3.8.2/Python / ...

#1 楼

在邮件列表上的Magnus Lycka答案中:

您可以使用“ python -u”
(或#!/ 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'



评论


原始sys.stdout仍可作为sys .__ stdout__获得。万一您需要它=)

– Antti Rasinen
08年9月20日在9:26

#!/ usr / bin / env python -u不起作用!看这里

– Wim
2012年12月10日,0:11

__getattr__只是为了避免继承?!

–弗拉基米尔·凯列舍夫(Vladimir Keleshev)
13年4月24日在7:33

需要注意的一些注意事项:正如我注意到的那样,输出缓冲的工作方式有所不同,具体取决于输出是进入tty还是另一个进程/管道。如果使用tty,则在每个\ n之后将其刷新,但在管道中对其进行缓冲。在后一种情况下,您可以使用这些冲洗溶液。在Cpython中(不在pypy中!!!):如果在sys.stdin中用for行遍历输入,... ... for循环将在运行循环主体之前收集许多行。尽管它是批处理,但其行为类似于缓冲。而是在true时执行:line = sys.stdin.readline()

–tzp
2013年6月10日12:35

@tzp:您可以使用iter()代替while循环:for iter(pipe.readline,''):中的行。在Python 3上不需要用它来插入管道:尽快产生。

– jfs
13年11月29日在17:11

#2 楼

我宁愿将答案放在如何刷新打印功能的输出中?还是在Python的print函数中调用缓冲区时会刷新该缓冲区?但是由于它们被标记为与此缓冲区的重复(我不同意),因此我将在此处回答。

因为Python 3.3 ,print()支持关键字参数“ flush”(请参阅​​文档):

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


评论


为我工作!谢谢!

–先生iPad Newton
12月8日8:24

#3 楼

# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
import io, os, sys
try:
    # Python 3, open as binary, then wrap in a TextIOWrapper with write-through.
    sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0), write_through=True)
    # If flushing on newlines is sufficient, as of 3.7 you can instead just call:
    # sys.stdout.reconfigure(line_buffering=True)
except TypeError:
    # Python 2
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)


信用:“ Sebastian”,在Python邮件列表中。

评论


在Python3中,您可以使用冲洗代码覆盖打印函数的名称。虽然这是一个肮脏的把戏!

–meawoppl
2014年1月22日18:50

@meawoppl:自Python 3.3起,您可以将printf()参数传递给print()函数。

– jfs
15年8月25日在9:23

编辑响应以显示响应在最新版本的python中无效

–迈克
18/12/10在23:51

在3.6.4中,os.fdopen(sys.stdout.fileno(),'wb',0)(注意b代表二进制)和flush = True都对我有用。但是,如果您使用子进程来启动另一个脚本,请确保已指定python3(如果已安装多个python实例)。

–not2qubit
18/12/13在14:36



@ not2qubit:如果使用os.fdopen(sys.stdout.fileno(),'wb',0),则最终得到的是二进制文件对象,而不是TextIO流。您必须将TextIOWrapper添加到混合中(确保启用write_through以消除所有缓冲区,或者使用line_buffering = True仅刷新行)。

–马丁·彼得斯(Martijn Pieters)♦
19年11月11日,11:55



#4 楼

是的。

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

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

#5 楼

这与CristóvãoD. Sousa的答案有关,但我尚无法评论。 />
import functools
print = functools.partial(print, flush=True)


之后,打印将始终直接刷新输出(给定flush除外)。

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

注意(b)仅在模块中有效/ script定义的位置。编写模块时这可能会很好,因为它不会与stderr混为一谈。

Python 2不提供sys.stdout参数,但是您可以仿真Python 3型flush函数,如此处所述https://stackoverflow.com/a/27991478/3734258。

评论


除了python2中没有flush kwarg。

–o11c
17年5月5日在5:19

@ o11c,是的,您是对的。我确定我已经测试过了,但是某种程度上我似乎很困惑(:我修改了答案,希望现在还好。谢谢!

– Tim
17年5月12日在10:41



#6 楼

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并不是一个好主意,因为这是放置不可释放周期的地方,您可能需要检查一下对于那些。)

评论


如果某些人建议,旧的stdout仍然生活在sys .__ stdout__上,那么就不需要垃圾了吧?这是一个很酷的把戏。

–托马斯·阿勒
2014年2月28日在10:17

就像@Federico的答案一样,这不适用于Python 3,因为它将引发异常ValueError:调用print()时不能具有未缓冲的文本I / O。

– gbmhunter
18年7月18日在16:57

起初,您的“另一种可能性”似乎是最可靠的解决方案,但是不幸的是,在另一个线程在sys.stdout.close()之后和os.dup2(temp_fd,fileno)之前调用open()的情况下,它遇到了竞争问题)。当我尝试在ThreadSanitizer下使用您的技术时,我发现了这一点。当dup2()与open()像这样竞争时,EUPY会失败,从而使失败更加严重。参见stackoverflow.com/questions/23440216/…

–唐·哈奇
18-10-30在7:01

#7 楼

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


评论


运行两次,它在Windows上崩溃:-)

– Michael Clerx
15年3月20日在12:32

@MichaelClerx嗯,永远记得关闭文件xD。

–user3917838
2015年12月5日,下午3:31

Raspbian 9上的Python 3.5给我OSError:[Errno 29]非法查找行sys.stdout = os.fdopen(sys.stdout.fileno(),'a +',buf_arg)

–sdbbs
19-09-20在8:23

#8 楼

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

#9 楼

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

stdbuf -oL python <script>

评论


行缓冲(-oL使能)仍在缓冲中-请参阅f / estackoverflow.com/questions/58416853/…,询问为什么end =''使输出不再立即显示。

–查尔斯·达菲(Charles Duffy)
19-10-16在15:47



是的,但是默认的是行缓冲(使用tty),因此假设输出完全没有缓冲,编写代码是否有意义—也许最好在显着的地方显式打印(...,end ='',flush = True)? OTOH,当多个程序同时写入相同的输出时,权衡往往从看到立即的进展转向减少输出混淆,并且行缓冲变得有吸引力。因此,也许最好不要在外部编写显式刷新和控制缓冲?

–贝尼(Beni Cherniavsky)-帕斯金(Paskin)
5月11日9:35

我想不是。流程本身应该决定何时,为什么调用冲洗。外部缓冲控制是强制性的解决方法

–肌瘤
5月13日7:27

#10 楼

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

_orig_print = print

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


如注释中所指出,可以通过绑定来简化通过functools.partial将flush参数转换为值:

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


评论


只是想知道,但这不是functools.partial的完美用例吗?

– 0xC0000022L
19年6月24日在11:09

感谢@ 0xC0000022L,这使它看起来更好! print = functools.partial(print,flush = True)对我来说很好。

– MarSoft
19年8月13日在12:04

@ 0xC0000022L确实,我已经更新了帖子以显示该选项,感谢您指出

–奥利弗
19年8月13日在14:57



如果您希望将其应用到任何地方,请导入内建函数; Builtins.print =局部(print,flush = True)

–珀金斯
19-10-29在1:52

#11 楼

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


评论


有一个等效的Windows:stackoverflow.com/questions/881696/…

–东武
2011年1月23日在1:41

O_SYNC与这个问题所要询问的用户空间级缓冲完全无关。

– Apenwarr
2012年4月25日在7:21

#12 楼

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

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导入的。

#13 楼

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

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


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

#14 楼

有效而不会崩溃的变体(至少在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)


评论


您确定没有缓冲吗?

–量子
2012年10月21日,下午3:52

您是否应该检查sys.stdout是sys .__ stdout__而不是依赖具有名称属性的替换对象?

–leewz
2014年1月31日上午8:07

如果Gunicorn由于某些原因不尊重PYTHONUNBUFFERED,则此方法非常有用。

–布赖恩·阿苏加(Brian Arsuaga)
2015年12月30日下午5:41

#15 楼

(我发表了评论,但由于某种原因迷失了。所以,再次:)


我注意到,CPython(至少在Linux上)的行为取决于输出的位置。如果是tty,则在每个'\n'后刷新输出。
如果是管道/进程,则将其缓冲,您可以使用基于flush()的解决方案或上面建议的-u选项。 br />
与输出缓冲稍微相关:
如果使用
for line in sys.stdin:
...


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

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

评论


这是您的评论。这可能是旧版Python的错误。您能提供示例代码吗?类似于sys.stdin中的行与iter中的行(sys.stdin.readline,“”)

– jfs
2013年6月19日15:40

对于sys.stdin中的行:print(“ Line:” + line); sys.stdout.flush()

–tzp
2013年6月21日12:19



它看起来像是预读错误。如果stdin是管道,则仅应在Python 2上发生。我之前的注释中的代码演示了该问题(因为sys.stdin中的行提供了延迟的响应)

– jfs
2015年8月25日在9:21



#16 楼

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


评论


当您随后尝试使用标准重定向捕获输出却发现您什么都没捕获时,这可能会造成很大的混乱。 ps。您的标准输出被加粗了。

–自由空间
08-09-20在10:00

关于有选择地打印到stderr的一个大注意事项是,这会导致行显得不正确,因此,除非您也有时间戳记,否则这可能会造成混乱。

–haridsv
2011年10月30日18:13