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.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 声明中,并赋予文件可执行权限后直接运行,这与在命令行指定 -u
选项是等价的。
#!/usr/bin/env python3 -u
chmod u+x test.py
./test.py
其他方法
Stack Overflow 上有一个高票答案,利用 sys.stdout
是文件对象的特性,通过 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
原因不明,不推荐使用。