[技巧篇] 如何在不會的情況下解釋 Python 設置文件的緩衝的問題
- 2019 年 10 月 4 日
- 筆記
美好的中秋,朋友圈在享受悠閑周末的時候,剛入門 Python 的程式設計師小R在公司埋頭加班,不知時間幾何,一不小心把下周的工作捯飭完了,心情愉悅,不能自拔。
這不,周一的早上,小R吹著口哨,泡著枸杞,幻想著划水到飛起,嘴角抑制不住的咧到了耳朵根兒,翹起了二郎腿。
正抖腿抖到舒坦的時候,微信收到了一條消息,點開一看,消息來自一個熟悉又陌生的名字,在記憶的排水溝里使勁掏了掏,好不容易對號入座。一個許久未曾聯繫的同學小K,消息只有兩個字:
「在嗎?」
看著這兩個字, 小R眉頭一皺,發現事情

怎麼回事?他這是要幹嘛?為什麼要問在嗎?
是要借錢嘛?是要結婚嘛?是要...

掙扎良久,小R放下了二郎腿,按下了鍵盤:
「在」
此時心裡已經在自導自演各種應對場景:
「要是借錢,我就...」
「要是結婚,我就...」
「....」

有一條消息過來,在它閃動的第24 次小R打開了聊天窗口:「聽說你是 Python 大佬,我有一個問題想問你。」
嘿,嚇死我了,你早說啊,此時小R內心一陣竊喜:「哎呀,我這麼低調,竟然被人發現大佬的身份啦,看來我還要再低調一點,哈哈哈哈哈哈...」
低調的小R低調的在窗口敲下了如下內容:
「哎呀,沒有啦,你有什麼問題,隨便問,沒有我不會的。」

enter 一鍵發送。
小K:「太好了,Python 裡面怎麼設置文件的緩衝啊?」
小R:「....」
文件我懂,緩衝我懂,怎麼設置,我哪知道怎麼設置...
小K:「還在嗎?」
好想說不在,當然認真負責的小R當然不會這樣,有句偉大的「名言」怎麼說的來著:自己的低調,跪著也要低調完...
於是,小R默默的打開了 Google...
00.文件的緩衝
如何設置文件的緩衝,先要知道什麼是文件的緩衝:
當我們將文件內容寫入到硬體設備的時候,我們需要系統調用(系統調用也就是向作業系統申請一個服務,作業系統響應以後,幫助我們調用硬體的驅動程式)來完成寫操作,這類操作我們也就是 I/O 操作。I/O 操作很耗時,為了提高效率,我們就要減少 I/O 操作的次數,我們使用的手段就是為文件設置一個緩衝區。
對於磁碟這種塊設備,它的讀寫不是一個一個的位元組,而是按「塊」。假設一個「塊」的大小是 4096 個位元組,你寫入一個位元組,或者寫入 4096 個位元組,都需要一次 I/O 操作,設置緩衝區,當寫入的數據不足一個「塊」大小時,都放入到緩衝區當中,等湊夠了一個「塊」的數據量,再調用一次 I/O 操作,這樣達到減少 I/O 操作的目的。
01.Python中文件對象的緩衝行為
文件的緩衝一般分為「全緩衝」、「行緩衝」、「無緩衝」。
「全緩衝」就是我在上面說的,緩衝區有一定大小,數據湊齊了這個大小就進行一次系統調用;「行緩衝」是在某些終端設備中上使用,碰到換行符進行一次系統調用;「無緩衝」是在一些不希望進行緩衝的設備上,比如串口設備,我們就需要及時把數據發送到串口上去。
下面我們就來看一下,在 Python 中默認的文件對象緩衝行為是怎樣的。
首先我們創建一個 test.txt 的文件,以「只寫」的形式打開:
f = open('test.txt','w')然後我們來觀察一下文件中的內容(此時應該為空):

接下來我們向 test.txt 中寫入一些內容:
f = open('test.txt','w') f.write('abc')我們再來看一下文件中的內容:

仍然沒有輸出,這就意味著 「abc」 並沒有真實的寫入到磁碟中,而是進入到了緩衝區,其實到這你可以探測一下緩衝區的大小,通常一個「塊」的大小為 4096 個位元組,你可以嘗試寫入來試驗,直到 tail -f test.txt 有內容顯示為止。
這就是普通文件默認的緩衝行為,緩衝區的大小是根據平台和自身的屬性相關的。在某些時候,我們需要改變緩衝區的大小,該怎麼做呢?
其實很好解決:在 open 函數中,有個 buffering 參數,可以讓這個問題迎刃而解。
我們將 buffering 設置為大於 1 的整數 n(n 為緩衝區的大小),這就是「全緩衝」;將 buffering 設置為 1,這就是「行緩衝」;將 buffering 設置為 0,這就是「無緩衝」。
接下來我們就嘗試一下:
f = open('test1.txt','w',buffering=1024) f.write('*'*512)我們用同樣的「只讀」方式創建 test1.txt,設定緩衝區大小為 1024,我們先寫入 512 個 「*」,接下來看一下文件的內容:

接下來我們就湊齊 1024 個位元組:
f = open('test1.txt','w',buffering=1024) f.write('*'*512) f.write('*'*511) f.write('/')此時文件的內容為:

這就全部輸出出來了。
同理,對於「行緩衝」和「無緩衝」也是類似的操作,改變相應 buffering 的值即可,只是對於「行緩衝」,記得要寫入換行符的時候才會在文件中顯示出內容,感興趣的可以嘗試一下。