What?
在 Python 程序中,使用 print 輸出調試信息的做法非常常見,但有的時候我們需要將 print 的內容改寫到其他位置,比如一個文件中,便于隨時排查。
但是又不希望大面積替換 print 函數,這就需要一些技巧實現。
一種典型的做法是在代碼開始的地方增加這樣的代碼:
def log_to_file(* args):
# write all args to some a file
pass
print = log_to_file
修改 print 方法,之后的 print 實際上就不是內置方法了。
在 Linux 下也可以通過 shell 的輸出重定向,將 print 的內容寫入文件:
python3 your_script.py >> log.txt
其實在 Python 代碼中,也可以通過輸出重定向技術,直接實現上面的目的。
重定向 stdout
stdout 是系統的標準輸出,通常是 shell 命令行。如果我們用其他對象覆蓋 sys.stdout 就可以實現輸出的重定向。
Python 使用任意實現了 write 方法的對象作為標準輸出對象。
下面定義了一個 OutputWrapper:
class OutputWrapper:
def __init__(self, to_file):
self._to = to_file
def write(self, data):
# let's write to log file with some extra string.
self._to.write("-----")
self._to.write(data)
它實現了 write 方法,它將輸出內容寫入 to_file, 我們再多做一點工作,在每次調用 print 前插入一段 “-----”。
下面我們用 OutputWrapper 實例替換原生 sys.stdout:
import sys
if __name__ == '__main__':
# the log file name
logname = 'log.txt'
with open(logname,'a') as logfile:
# save original stdout
original = sys.stdout
# redirect stdout
sys.stdout = OutputWrapper(logfile)
# output
print('line 1')
print('line 2')
# restore stdout
sys.stdout = original
運行時命令行的輸出消失了,如果查看 log.txt :
-----line 1-----
-----line 2-----
原來 print 的內容已經寫到文件中了,此時 log.txt 就相當于命令行輸出了。
為什么 “line1” 和 “line2” 后面也多了額外輸出呢?這是因為 print 方法會自動在尾部追加輸出一個 ‘\n’,又調用了一次 print(’\n’)。
How ?
這種重定向技術是如何實現的?其實一切就在最熟悉不過的 print 方法中。
print 方法是 Python 的內建函數,其原型為:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)第一個參數是不定參數,表示 print 的對象;
sep 是多個輸出對象之間的分隔符,所以 print(‘a’, ‘b’) 會輸出 “a b”,這是因為 sep 默認是空格,你也可以設置為其他字符串;
end 是行位字符,默認是換行,這也是 print 總是一行的原因,如果不希望換行,可以用參數 end = “”;
file 非常重要,表示 print 方法的輸出對象,最終的輸出工作是通過 file.write 實現的,默認情況下 file = sys.stdout。
flush 與輸出緩沖有關,暫時不必關心。
前面的輸出重定向可以總結為這樣的流程:修改了 sys.stdout --> print 默認 file = sys.stdout --> 間接修改了 print 的輸出對象。
原來如此!Built-in Functions?docs.python.org
首發公眾號 “江川Go”,關注了解程序員的燒腦日常。