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.write
和 print
的行为。
不缓存直接输出
如果不想让 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
强制刷新缓冲区并上屏,并给出了使用自定义类复写 write
和 writelines
方法的代码。
#!/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
原因尚不明确,不建议使用。