Python快取重用機制

2020-07-16 10:05:04
Python 緩衝機制是為提高程式執行的效率服務的,實際上就是在 Python 直譯器啟動時從記憶體空間中開闢出一小部分,用來儲存高頻使用的資料,這樣可以大大減少高頻使用的資料建立時申請記憶體和銷毀時復原記憶體的開銷。

Python 在儲存資料時,會根據資料的讀取頻繁程度以及記憶體占用情況來考慮,是否按照一定的規則將資料儲存快取中。那麼問題來了,記憶體重用機制適用於哪些基本資料型別呢?

表 1 羅列了 Python 是否將指定資料存入快取中的規則:

表 1 Python 快取重用規則
資料型別 是否可以重用 生效範圍
範圍在 [-5, 256] 之間的小整數 如果之前在程式中建立過,就直接存入快取,後續不再建立。 全域性
bool 型別
字串型別資料
大於 256 的整數 只要在本程式碼塊內建立過,就直接快取,後續不再建立。 本程式碼塊
大於 0 的浮點型小數
小於 0 的浮點型小數 不進行快取,每次都需要額外建立。
小於 -5 的整數
 
下面直接通過一段程式來演示 Python 快取機制的規則。
#範圍在 [-5, 256] 之間的小整數
int1 = -5
int2 = -5
print("[-5, 256] 情況下的兩個變數:", id(int1), id(int2))

#bool型別
bool1 = True
bool2 = True
print("bool型別情況下的兩個變數:",id(bool1),id(bool2))

#對於字串
s1 = "3344"
s2 = "3344"
print("字串情況下的兩個交量", id(s1), id(s2))

#大於 256 的整數
int3 = 257
int4 = 257
print("大於 256 的整數情況下的兩個變數", id(int3), id(int4))

#大於 0 的浮點數
f1 = 256.4
f2 = 256.4
print("大於 0 的浮點數情況下的兩個變數", id(f1), id(f2))

#小於 0 的浮點數
f3 = -2.45
f4 = -2.45
print("小於 0 的浮點數情況下的兩個變數", id(f3), id(f4))

#小於 -5 的整數
n1 = -6
n2 = -6
print("小於 -5 的整數情況下的兩個變數", id(n1), id(n2))
注意,此程式中,大量使用 id() 內建函數,該函數的功能是獲取變數(物件)所在的記憶體地址。執行該程式,其輸出結果為:

[-5, 256] 情況下的兩個變數: 1792722416 1792722416
bool型別情況下的兩個變數: 1792241888 1792241888
字串情況下的兩個交量 2912801330712 2912801330712
大於 256 的整數情況下的兩個變數 2912801267920 2912801267920
大於 0 的浮點數情況下的兩個變數 2912762210728 2912762210728
小於 0 的浮點數情況下的兩個變數 2912762211016 2912762211040
小於 -5 的整數情況下的兩個變數 2912801267952 2912801267984

以上輸出結果中,每行都輸出了 2 個相對應的變數所在的記憶體地址,如果相等,則表明 Python 內部對其使用了快取機制,反之則沒有。讀者可對照以上輸出結果來理解表 1 中有關變數快取機制的規則。

另外,對於表 1 中所提到的程式碼塊,Python 中的函數和類都被認為是在程式中開闢了一塊新的程式碼塊。以函數為例,函數內部的程式碼分屬一個程式碼塊,函數外部的程式碼屬於另一個程式碼塊。

有關函數的具有用法,後續章節會詳細介紹,這裡讀者只需要知道函數中包含的程式碼,屬於一個新的程式碼塊即可。

由表 1 可以看到,Python 快取機制在不同的程式碼塊中也會有不同的表現。舉一個例子,在上面例子程式碼的基礎上,繼續編寫如下程式:
def fun():
    #[-5,256]
    int1 = -5
    print("fun中 -5 的儲存狀態",id(int1), id(int2))
    
    #bool型別
    bool3 = True
    print("fun中 bool 型別的儲存狀態",id(bool3),id(bool2))
    
    #字串型別
    s1 = "3344"
    print("fun 中 3344 字串的儲存狀態", id(s1), id(s2))

    #大於 256
    int3 = 257
    print("fun中 257 的儲存狀態", id(int3), id(int4))

    #浮點型別
    f1 = 256.4
    print("fun 中 256.4 的儲存狀態",id(f1), id(f2))

    #小於 -5
    n1 = -6
    print("fun 中 -6 的儲存狀態", id(n1), id(n2))
   
fun()
輸出結果為:

fun中 -5 的儲存狀態 1792722416 1792722416
fun中 bool 型別的儲存狀態 1792241888 1792241888
fun 中 3344 字串的儲存狀態 1976405206496 1976405206496
fun中 257 的儲存狀態 1976405225648 1976405225680
fun 中 256.4 的儲存狀態 1976394459752 1976394459872
fun 中 -6 的儲存狀態 1976404744880 1976405225744

根據輸出結果可以分析出:
  1. 從 -5 、bool 型別以及字串 "3344" 的輸出結果可以得知,無論是在同一程式碼塊,還是不同的程式碼塊,它們都使用相同的快取內容;
  2. 從 257 和 256.4 的輸出結果可以得知,如果位於同一程式碼塊,則使用相同的快取內容;反之,則不使用;
  3. 從 -6 的輸出結果得知,Python 沒有對其快取進行操作。