stdout 中的缓存
首先来看一个例子:
1
2
3
4
5
6
7
8
| #!/usr/bin/env python3
import sys
sys.stdout.write("stdout1 ")
sys.stderr.write("stderr1 ")
sys.stdout.write("stdout2 ")
sys.stderr.write("stderr2 ")
|
将以上代码保存成 Python 文件运行。实际输出并不是:
1
| stdout1 stderr1 stdout2 stderr2
|
而是:
1
| stderr1 stderr2 stdout1 stdout2
|
原因是,虽然 stderr 和 stdout 默认都指向屏幕输出,但是:
- stderr 是无缓存的,程序向 stderr 输出字符会直接打印出来;
- stdout 是有缓存的,只有遇到换行或者积累到一定的大小,才会打印出来;
以上会影响 sys.stdout.write
和 print
的行为。
不缓存直接输出
如果不想让 stdout 缓存内容,有以下几种方案。
PYTHONUNBUFFERED
设置 PYTHONUNBUFFERED
环境变量为非空字符串:
1
| export PYTHONUNBUFFERED=1
|
或者在运行代码时指定:
1
| PYTHONUNBUFFERED=1 python3 test.py
|
python -u
在运行 Python 文件时指定 -u
选项,效果等同于 PYTHONUNBUFFERED
环境变量。
或者将 -u
选项加入到 Python 文件的 shebang 声明中,并赋予文件可执行权限并直接运行,与在运行 Python 文件时指定 -u
选项是等价的。
1
| #!/usr/bin/env python3 -u
|
1
2
| chmod u+x test.py
./test.py
|
其他
在 Stack Overflow 上,有个高票答案利用了 sys.stdout
本质上是 file-object 的原理,利用 sys.stdout.flush
强制刷新缓冲区并上屏,并给出了使用自定义类复写 write
和 writelines
方法的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #!/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 中输出为:
1
| stdout1 stdout2 stderr1 stderr2
|
原因尚不明确,不建议使用。
参考链接