Python中stdout的输出缓存

stdout 中的缓存

首先来看一个例子:

#!/usr/bin/env python3

import sys

sys.stdout.write("stdout1 ")
sys.stderr.write("stderr1 ")
sys.stdout.write("stdout2 ")
sys.stderr.write("stderr2 ")

将以上代码保存成 Python 文件运行。实际输出并不是:

stdout1 stderr1 stdout2 stderr2

而是:

stderr1 stderr2 stdout1 stdout2

原因是,虽然 stderr 和 stdout 默认都指向屏幕输出,但是:

  • stderr 是无缓存的,程序向 stderr 输出字符会直接打印出来;
  • stdout 是有缓存的,只有遇到换行或者积累到一定的大小,才会打印出来;

以上会影响 sys.stdout.writeprint 的行为。

不缓存直接输出

如果不想让 stdout 缓存内容,有以下几种方案。

PYTHONUNBUFFERED

设置 PYTHONUNBUFFERED 环境变量为非空字符串:

export PYTHONUNBUFFERED=1

或者在运行代码时指定:

PYTHONUNBUFFERED=1 python3 test.py

python -u

在运行 Python 文件时指定 -u 选项,效果等同于 PYTHONUNBUFFERED 环境变量。

python3 -u test.py

或者将 -u 选项加入到 Python 文件的 shebang 声明中,并赋予文件可执行权限并直接运行,与在运行 Python 文件时指定 -u 选项是等价的。

#!/usr/bin/env python3 -u
chmod u+x test.py
./test.py

其他

在 Stack Overflow 上,有个高票答案利用了 sys.stdout 本质上是 file-object 的原理,利用 sys.stdout.flush 强制刷新缓冲区并上屏,并给出了使用自定义类复写 writewritelines 方法的代码。

#!/usr/bin/env python3

import sys

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)

sys.stdout = Unbuffered(sys.stdout)

sys.stdout.write("stdout1 ")
sys.stderr.write("stderr1 ")
sys.stdout.write("stdout2 ")
sys.stderr.write("stderr2 ")

但是经过测试,该方法在 Python 2.7.15 中输出正常,但在 Python 3.7 中输出为:

stdout1 stdout2 stderr1 stderr2

原因尚不明确,不建议使用。

参考链接

目录