看到本文标题也许要奇怪了,Python 的 print 难道不是也上可以看到结果的吗?在 Python shell 下只要
>> print('Hello world!')
Hello world!
不就立马能看到控制台输出的 "Hello world!" 吗。或者是一个 Python 脚本文件 hello.py
import time
for i in range(3):
print('Hello {}'.format(i))
time.sleep(3)
然后执行 python hello.py 的话,我们也同样能看到在控制台下在预定的每 3 秒输出一行
Hello 0
Hello 1
Hello 2
但执行下面的命令试图重定向输出到文件时的效果就不一样了
$ python hello.py > bb.txt &
$ tail -f bb.txt
我们希望内容输出到文件 bb.txt 中,并且 tail -f bb.txt 实时看到每 3 秒有一行输出的效果。但实际却不好呀我们所愿,tail -f bb.txt 在 9 秒钟内没有任何输出,最后一瞬间看到所有三行内容同时输出。这样的话, 我们在使用重定向 print 输出日志到文件的同时,就无法实时观测到执行效果。究其原因是 print 的内容重定向到文件时有一个缓冲区,只有缓冲区满后才会把当前缓冲区中所有内容输出到文件,这就造成了 tail -f 延迟。
知道了原因就好办了,那就把缓冲区大小设置为 0 吧,或者每次 print 后 flush 一次缓冲区强型输出就是了。因此可有如下解决办法
Python 3.3+版本
在 Python 3.3 及后版本的 print 函数引进了一个 flush 参数,它默认为 False, 只要每次指定为 True就行了
1
2
3
4
5
importtime
foriinrange(3):
print('Hello {}'.format(i),flush=True)
time.sleep(3)
现在
$ python hello.py > bb.txt &
$ tail -f bb.txt
就能看到与直接执行 python hello.py 同样的效果了,输出是实时的,每 3 秒有一行输出。
Python 3.3 之前的版本(含 Python 2)
Python 都要 3.9 了,Python 3.3 之前的版本几乎是不可能再用,但还是提一下在这之前如何刷新输出缓冲,见如下代码
1
2
3
4
5
6
7
importtime
importsys
foriinrange(3):
print'Hello {}'.format(i)
sys.stdout.flush()# 需要紧随每个 print 语句后执行一次
time.sleep(3)
改变 print 的 flush 参数值为 True
针对于 Python 3.3 +,如果想 print 重定向输出到文件时能看到实时效果的话,我们必须给每个 print 加上 flush=True 的参数。这有些麻烦,所以进一步,能否改变 print 的 flush 的参数默认值为 True 呢?没问题,瞧下面的代码
1
2
3
4
5
6
7
importtime
importfunctools
print=functools.partial(print,flush=True)
foriinrange(3):
print('Hello {}'.format(i))
time.sleep(3)
关闭输出缓冲区
正如前面那样,有缓冲区时只要每次 flush 一下缓冲就立即输出,另一方面我们还可直接把缓冲区关闭掉,即缓冲区大小为 0. 需要设置环境变量 PYTHONUNBUFFERED=TRUE
类 Unix系统
export PYTHONUNBUFFERED=TRUE
Windows系统
set PYTHONUNBUFFERED=TRUE
设置了 PYTHONUNBUFFERED=TRUE 后,执行最初的代码
1
2
3
4
importtime
foriinrange(3):
print('Hello {}'.format(i))
time.sleep(3)
$ python hello.py > bb.txt &
$ tail -f bb.txt
就能实时看到一行行的输出了,这种方式对于第三方的 Python 库中的 print 也管用。
还有一种设置 PYTHONUNBUFFERED=TRUE 的方式,就是启动 Python 解释器时加上 -u 参数,这种方式只影响当前程序
$ python -u hello.py > bb.txt
$ tail -f bb.txt
设置环境变量 PYTHONUNBUFFERED=TRUE 与 -u 参数都不具有代码侵入性,相比而言 -u 是一种更为完美的解决方式,因它不会影响到其他的 Python程序。
最后小结一下无缓冲输出的各种解决方案
print('hello', flush=True) 指定 flush 参数为 True
print = functools.partial(print, flush=True) 改变 print 的 flush 默认值为 True
设置环境变量 PYTHONUNBUFFERED=TRUE
python -u 启动 PYTHONUNBUFFERED=TRUE 的 Python解释器
链接: