禁用输出缓冲

Eli*_*sky 487 python stdout buffered

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

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

建议到目前为止:

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

是否有任何其他方式来设置一些全局标志sys/ sys.stdout程序执行过程中?

Seb*_*Seb 410

来自Magnus Lycka的回复邮件列表:

您可以使用"python -u"(或#!/ usr/bin/env python -u等)或通过设置环境变量PYTHONUNBUFFERED来跳过整个python进程的缓冲.

您还可以将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'
Run Code Online (Sandbox Code Playgroud)

  • 原始sys.stdout仍可用作sys .__ stdout__.万一你需要它=) (69认同)
  • `#!/ usr/bin/env python -u`不起作用!! 见[这里](http://stackoverflow.com/q/3306518/674039) (37认同)
  • 保存一些令人头疼的注意事项:正如我所注意到的,输出缓冲的工作方式不同,具体取决于输出是转到tty还是其他进程/管道.如果它转到tty,则在每个*\n*之后刷新,但在管道中它被缓冲.在后一种情况下,您可以使用这些冲洗解决方案.在Cpython中(不是pypy !!!):如果你在sys.stdin中使用**替换输入:**...那么for循环将在循环体运行之前收集许多行.这将表现得像缓冲,虽然它是相当批处理.相反,执行**而true:line = sys.stdin.readline()** (28认同)
  • `__getattr__`只是为了避免继承?! (6认同)
  • @tzp:你可以使用`iter()`而不是`while`循环:`for iter in iter(pipe.readline,''):`.在Python 3中你不需要它,其中`for line in pipe:`尽快产生. (5认同)
  • 那么,伙计们,禁用输出缓冲的后果是什么?你什么时候不想? (2认同)
  • @tzp:当使用`python myscript.py |时,这种不同的行为特别令人生气。tee logfile.txt`-目的是在记录日志的同时查看您的操作! (2认同)
  • @Halst 否。使用`__getattr__` 允许它与*any* 流一起使用,而不仅仅是您打算使用的任何特定类。 (2认同)

Cri*_*usa 101

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

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

print('Hello World!', flush=True)
Run Code Online (Sandbox Code Playgroud)


Fed*_*oni 75

# 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, and write through
    # everything. Alternatively, use line_buffering=True to flush on newlines.
    unbuffered = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0), write_through=True)
except TypeError:
    # Python 2
    unbuffered = os.fdopen(sys.stdout.fileno(), 'w', 0)
Run Code Online (Sandbox Code Playgroud)

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

第三方编辑

最新版本的Python 3不支持

  • @meawoppl:你可以从Python 3.3开始将`ftush = True`参数传递给`print()`函数. (14认同)
  • 如果刷新换行符就足够了,从 Python 3.7 开始,您可以简单地调用 `sys.stdout.reconfigure(line_buffering=True)` (2认同)

Bri*_*ian 51

是的.

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

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


tim*_*tim 17

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

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

import functools
print = functools.partial(print, flush=True)
Run Code Online (Sandbox Code Playgroud)

之后,print将始终直接刷新输出(除非flush=False给出).

注意,(a)这只是部分回答了问题,因为它没有重定向所有输出.不过,我想print是一个用于创建输出的最常用的方法stdout/ stderrPython中,所以这两条线可能覆盖大部分的用例.

注意(b)它只适用于您定义它的模块/脚本.这在编写模块时可能很好,因为它不会弄乱模块sys.stdout.

Python 2不提供flush参数,但您可以模拟Python 3类型的print函数,如/sf/answers/1959403491/所述.

  • 除了python2中没有`flush` kwarg。 (2认同)

Mar*_*orn 14

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"])
Run Code Online (Sandbox Code Playgroud)

如果不保存旧的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
Run Code Online (Sandbox Code Playgroud)

另一种可能性是:

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)
Run Code Online (Sandbox Code Playgroud)

(附加到gc.garbage并不是一个好主意,因为它是放置不合理周期的地方,你可能想要检查那些.)

  • 如果旧的`stdout`仍然存在于某些人所建议的`sys .__ stdout__`中,那么垃圾就没有必要了,对吧?这是一个很酷的技巧. (2认同)
  • 与@Federico 的回答一样,这不适用于 Python 3,因为它会在调用 `print()` 时抛出异常 `ValueError: can't have unbuffered text I/O`。 (2认同)

Nat*_*han 12

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


小智 12

以下适用于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)
Run Code Online (Sandbox Code Playgroud)

  • 运行两次,它在 Windows 上崩溃:-) (2认同)

dyo*_*mas 7

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

stdbuf -oL python <script>

  • 行缓冲(如“-oL”启用)仍在缓冲 - 请参阅 f/e /sf/ask/4089179741/,询问为什么“end=” ` 使输出不再立即显示。 (3认同)

Oli*_*ver 5

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

_orig_print = print

def print(*args, **kwargs):
    _orig_print(*args, flush=True, **kwargs)
Run Code Online (Sandbox Code Playgroud)

如注释中所指出的,您可以通过以下方式将flush参数绑定到一个值来简化此操作functools.partial

print = functools.partial(print, flush=True)
Run Code Online (Sandbox Code Playgroud)

  • 如果您希望它适用于所有地方,请“导入内置函数;” builtins.print=partial(print,flush=True)` (4认同)
  • 只是想知道,但这不是 `functools.partial` 的完美用例吗? (3认同)