Python的數據緩存機制探究

在學習Python過程中,我們可能會偶爾會遇見下面的這些情況:

說明:
id()函數用來獲取某個變量/數據對象的內存地址;
is操作符用來判斷兩個變量/數據對象是否指向同一個內存地址。

(E1)在Python的IDLE交互式模式下,當兩個變量a和b同時賦值為256時,它們的內存地址是一致的,因此用a is b進行判斷時結果為True,但當它們同時賦值為257時,內存地址卻不一樣,a is b的結果為False。

Python的數據緩存機制探究

(E2)在Python的IDLE文件模式下,當a和b同時賦值為257時,它們的內存地址卻是一樣的。

Python的數據緩存機制探究

(E3)在Python的IDLE文件模式下,分別使用直接賦值和表達式賦值兩種方式對a和b賦值為257時,發現:直接賦值的內存地址是一樣的,而表達式賦值方式的地址是不一樣的。

Python的數據緩存機制探究

(E4)在Python中,對兩個變量a和b同時賦值為空元組時,它們的內存地址是一樣的,當它們同時賦值為空列表時,內存地址卻不一樣。

Python的數據緩存機制探究

(E5)在Python中,當兩個變量a和b同時賦值為不同的列表時,他們的內存地址肯定是不一樣,但它們中相同元素的地址可能是一樣的。

Python的數據緩存機制探究

......

以上所列情況僅僅是個例,並不能從中總結出比較完備的規律。在好奇心的驅使下,作者大量嘗試了各種數據類型的各種情況,並查閱了相關資料,才明白這是Python的緩存機制在作怪。什麼是Python的緩存機制呢?

一、什麼是Python緩存機制

大家都知道現實生活中很多場景都符合二八定律,在程序中也是一樣,經常被使用的數據對象可能會佔據所有數據對象使用頻率的80%,因此為了優化程序,提高程序的執行效率,Python解釋器會將常用數據對象放在緩存中,一旦使用時,將數據對象的引用傳遞過去即可。

Python緩衝機制是為提高程序執行的效率服務的,實際上就是在Python解釋器啟動時從內存空間中開闢出一小部分,用來存儲高頻使用的對象,這樣可以大大減少高頻使用的數據對象創建時申請內存和銷燬時撤銷內存的開銷。

那麼接下來的問題來了,什麼樣的數據類型會使用緩存呢?整數?浮點數?字符串?列表?

二、什麼數據類型會使用緩存?

在Python中,數據類型可以劃分為基本數據類型和組合數據類型,基本數據類型主要包括:整數、浮點數、複數、布爾、字符串,組合數據類型主要包括:元組、列表、集合、字典。

根據數據是否可變,數據類型又可以分為:可變類型和不可變類型,可變類型包括:列表、集合、字典,上述其他的都屬於不可變類型。

可變類型意味著在程序執行過程中可能會隨時發生變化,因此它就失去了緩存的意義,所以在Python中使用緩存的規律是:

(1)不可變數據類型可能會使用緩存,具體是否使用緩存會依賴於數據類型和數據本身的具體情況,在第三節中會對每種數據類型進行詳細討論。

(2)可變數據類型肯定不使用緩存,但可變數據類型裡面的元素如果是不可變類型的話,則內部元素可能使用緩存(例如上面的E5的例子)。從下面的代碼示例可以看出,當數據類型為列表、字典或集合時,無論它們是同為空或者有相同的數據,它們都佔用著不同的地址空間。

Python的數據緩存機制探究

三、不可變數據類型的緩存機制

從E1的示例可以看出,交互式模式下256是使用了緩存,但257卻沒有,然而通過E2可以看出,在文件式模式下,256和257都使用了緩存。但從E3可以看出如果257是由表達式計算得到的話,那麼它將不使用緩存。

通過以上的現象並經過作者大量的嘗試和查閱資料,作者認為:Python中緩存分為固定緩存和運行時緩存(作者自行總結起的名字,不喜勿噴,如果有更權威的說法還望留言告知)。固定緩存是在解釋器啟動時就創建的,比如整數的[-5, 256]、None、True、False。運行時緩存是指在程序執行過程中,解釋器為了優化程序數據對象的創建而設定的緩存,比如字符串、[-5, 256]以外的整數等。

3.1 整數

對於整數,有如下規律:

(1)Python中整數[-5, 256]為固定緩存,在解釋器啟動時就會創建,程序中只要是使用到該範圍內的數字,不管是直接賦值還是表達式計算得到的,都會使用固定緩存中的數據。

Python的數據緩存機制探究

打印-10到0的數據內存地址可以看到,[-10,-6]和[-5,0]使用的地址空間是不一樣的,這也印證了[-5, 256]是使用固定區域緩存的這一說法。同時還可以發現[-10,-6]在輸出時只使用了兩個內存地址,在交替使用,這是因為在示例中,循環變量i每次循環都會變化一次,輸出完後i就不再指向該數據,而指向了i+1,i就會很快被Python垃圾回收機制自動收回。

Python的數據緩存機制探究

(2)小於-5的整數永遠不會使用緩存,不論在交互式環境下還是在文件式環境下。

Python的數據緩存機制探究

Python的數據緩存機制探究

(3)對於大於256的整數,如果是由表達式計算得到的相同數值,肯定不會使用緩存。如果是直接賦值的情況下,在某些情況下可能會使用運行時緩存,分為兩種情況:

a、在交互式模式下,如果兩個變量不是同時賦值為同一個大於256的整數,那麼它們將不使用運行時緩存,如果兩個變量同步賦值或者在同一個代碼塊中,這時會使用運行時緩存。

Python的數據緩存機制探究

b、在文件式模式下,如果兩個變量同時屬於局部變量或者同時屬於全局變量,那麼它們可以使用運行時緩存,如果它們一個是全局變量,一個是局部變量,就不會使用運行時緩存。

Python的數據緩存機制探究

3.2 浮點數

對於浮點數,在文件式模式下,小於0.0的浮點數不會使用運行時緩存,只有>=0.0的浮點數才會使用運行時緩存。在交互式模式下時,除非同時賦值或者在同一個代碼塊下(>=0.0的浮點數才會使用運行時緩存),否則任何浮點數都不會使用運行時緩存。

Python的數據緩存機制探究

交互式模式下示例:

Python的數據緩存機制探究

3.3 字符串

對於字符串,是否使用運行時緩存存在以下規律(在文件式模式下):

(1)直接賦值的字符串,無論多長,是否包含特殊字符(#.-@等),都會使用運行時緩存。

Python的數據緩存機制探究

(2)通過表達式計算得到字符串:如果只包含字母(a-z,A-Z)和數字(0-9),並且最終的長度不超過20的情況下,也會使用運行時緩存。一旦長度超過20,將不使用緩存;如果最終字符串中包含特殊字符(#.-@等),不論長度多長,將不使用緩存。

Python的數據緩存機制探究

在交互式模式下(同時賦值/同一代碼塊除外),當字符串長度大於1時,只要字符串中有有特殊字符,就不會使用緩存。當字符串長度為1時,任何字符的字符串都會使用緩存。

3.4 True、False、None

任何情況下都使用固定緩存

3.5 元組

由於元組是可以嵌套不可變類型,因此Python中對於非空元組不使用緩存,但對空的元組使用運行時緩存。

Python的數據緩存機制探究

3.6 複數

這個類型作者從沒用過,就不做解讀了,歡迎好事者補充。

以上是作者對於Python緩存機制的總結,測試環境是Python3.5,不一定完整,也可能存在錯誤。歡迎大家進行討論,並提出寶貴意見,喜歡的朋友也可以關注本頭條號,【玩轉Python】將持續為大家提供優質的技術內容。


分享到:


相關文章: