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 是无缓冲的,每次写入都会立即输出;
  • 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 声明中,并赋予文件可执行权限后直接运行,这与在命令行指定 -u 选项是等价的。

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

其他方法

Stack Overflow 上有一个高票答案,利用 sys.stdout 是文件对象的特性,通过 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

原因不明,不推荐使用。

参考链接