詳解Python小數據池和代碼塊緩存機制

前言

本文除”總結”外,其餘均為認識過程;3.7.5;這部分官方文檔不知道在哪裡找,目前沒有找到,有誰知道的可以麻煩留言嗎? 謝謝瞭!

總結:

如果在同一代碼塊下,則采用同一代碼塊下的緩存機制;
如果是不同代碼塊,則采用小數據池的駐留機制;
需要註意的是,交互式輸入時,每個命令都是一個代碼塊;

實現 Intern 保留機制的方式非常簡單,就是通過維護一個字符串儲蓄池,這個池子是一個字典結構,編譯時,如果字符串已經存在於池子中就不再去創建新的字符串,直接返回之前創建好的字符串對象,
如果之前還沒有加入到該池子中,則先構造一個字符串對象,並把這個對象加入到池子中去,方便下一次獲取;

長度為0與1的字符串一定會被駐留;
字符串駐留發生在程序編譯時;
被駐留的字符串必須由 ASCll 字母, 數字以及下劃線組成;

1.代碼塊的緩存機制

Python 程序是由代碼塊構造的。塊是一個 Python 程序的文本,它是作為一個單元執行的。
代碼塊:一個模塊, 一個函數, 一個類, 一個文件等都是一個代碼塊;
交互方式:在 cmd 中進入 Python 解釋器裡面,輸入的每一條命令都是一個代碼塊;

Python 在執行同一個代碼塊的初始化對象的命令時,會檢查其值是否存在,如果存在,會將其重用;
滿足代碼塊的緩存機制則它們在內存中隻存在一個,即:id相同;
代碼塊的緩存機制的適用范圍: int(float),str,bool;

int(float): 任何數字在同一代碼塊下都會復用;
bool: True 和 False 在字典中會以 1,0 方式存在,並且復用;
str:同一代碼塊中,值相同的字符串在內存中隻存在一個:

s1 = '[email protected]!#*ewq'
s2 = '[email protected]!#*ewq'
print(s1 is s2)	 # True 

a1 = '[email protected]#$%#^%@$%' * 1
b1 = '[email protected]#$%#^%@$%' * 1
print(a1 is b1) # True

s1 = 'hah_' * 6
s2 = 'hah_' * 6
print(s1 is s2) # True

2.小數據池

Python 自動將 -5~256 的整數進行瞭緩存,當你將這些整數賦值給變量時,並不會重新創建對象,而是使用已經創建好的緩存對象;
Python會將滿足一定規則的字符串在字符串駐留池中,創建一份,當你將這些字符串賦值給變量時,並不會重新創建對象, 而是使用在字符串駐留池中創建好的對象;
bool 值就是 True,False,無論你創建多少個變量指向 True,False,它在內存中都隻存在一個;

小數據池也是隻針對 int(float),str,bool;
小數據池是針對不同代碼塊之間的緩存機制;

# cmd, -5~256 的小整數雖然不在同一代碼塊中, 但是它們適用小數據池機制
>>>a = 245
>>>b = 245
>>>a is b # True
# 長度為0與1的字符串一定會被駐留;
# 字符串駐留發生在程序編譯時;
# 被駐留的字符串必須由 ASCll字母, 數字以及下劃線組成;
>>>s1 = '@'
>>>s2 = '@'
>>>s1 is s2 # True

>>>s1 = ''
>>>s2 = ''
>>>s1 is s2 # True

>>>s1 = 'a_b_c'
>>>s2 = 'a_b_c'
>>>s1 is s2 # True

>>>s1 = 'a b_c'
>>>s2 = 'a b_c'
>>>s1 is s2 # False

>>>s1 = 'a_b_c' * 1
>>>s2 = 'a_b_c' * 1
>>>s1 is s2 # True

>>>s1 = 'abd_d23' * 3
>>>s2 = 'abd_d23' * 3
>>>s1 is s2 # True

>>>a, b = "some_thing!", "some_thing!"
>>>a is b # False

>>>a, b = "some_thing", "some_thing"
>>>a is b # True
a1 = 1000
b1 = 1000
a1 is b1 # True

class C1(object): 
   a = 100
   b = 100
   c = 1000
   d = 1000
 
 
class C2(object):
   a = 100
   b = 1000

print(C1.a is C1.b)  # True
print(C1.a is C2.a)  # True
print(C1.c is C1.d)  # True
print(C1.c is C2.b)  # False

3.優缺點

優點:值相同的字符串的(比如標識符),直接從池裡拿來用,避免頻繁的創建和銷毀,提升效率,節約內存;

缺點:拼接字符串、對字符串修改之類的影響性能;
因為是不可變的,所以對字符串修改不是 inplace 就地操作,要新建對象,這也是為什麼拼接多字符串的時候不建議用 + 而用 join();
join() 是先計算出所有字符串的長度,然後一一拷貝,隻 new 一次對象;

小整數對象池

為避免整數頻繁申請和銷毀內存空間,python 使用瞭小整數對象池,Python 對小整數的定義是 [-5, 256] ,這些整數對象是提前建立好的,不會被垃圾回收;
一個 Python 程序中,無論這個整數處於 LEGB 中哪個位置,所有位於這個范圍內的整數使用的都是同一個對象;

# 3.7.5, ipython7.18.1
a = -5
b = -5
a is b # True

a = -6
b = -6
a is b # False

a = 256
b = 256
a is b # True

a = 257
b = 257
a is b # Flase

大整數對象池

cmd 終端中,大整數每賦值一次,每次的大整數都會重新創建,Pycharm 中,每次運行時,所有代碼都加載到內存中,屬於一個整體,所以這個時候會有一個大整數對象池處於一個代碼塊的大整數是同一個對象;
c 和 d 處於一個代碼塊,而 C1.b 和 C2.b 分別有自己的代碼塊,所以不相等;

# cmd 終端
a = 1000
b = 1000
a is b # False
--------------------
class C1(object): 
   a = 100
   b = 100
   c = 1000
   d = 1000
 
 
class C2(object):
   a = 100
   b = 1000

print(C1.a is C1.b)  # True
print(C1.a is C2.a)  # True
print(C1.c is C1.d)  # True ?? 難道 cmd 中也有大整數池 ?? 類加載的時候是在一塊內存中,同值同地址 ?? 
print(C1.c is C2.b)  # False

# pycharm 等編輯器中
a = 1000
b = 1000
a is b # True
--------------------
class C1(object): 
   a = 100
   b = 100
   c = 1000
   d = 1000
 
 
class C2(object):
   a = 100
   b = 1000

print(C1.a is C1.b)  # True
print(C1.a is C2.a)  # True
print(C1.c is C1.d)  # True
print(C1.c is C2.b)  # False

字符串駐留機制

  Python 解釋器為瞭提高字符串使用的效率和使用性能,編譯時,使用瞭 intern(字符串駐留)技術來提高字符串效率,什麼是 intern 機制?即值同樣的字符串對象僅僅會保存一份,放在一個字符串儲蓄池中,是共用的,當然,肯定不能改變,這也決定瞭字符串必須是不可變對象(整數類型也是不可變對象)??,浮點數就不行 ;

簡單原理:

  實現 Intern 保留機制的方式非常簡單,就是通過維護一個字符串儲蓄池,這個池子是一個字典結構,編譯時,如果字符串已經存在於池子中就不再去創建新的字符串,直接返回之前創建好的字符串對象,如果之前還沒有加入到該池子中,則先構造一個字符串對象,並把這個對象加入到池子中去,方便下一次獲取。;
  但是,解釋器內部對intern 機制的使用策略是有考究的,有些場景會自動使用 intern ,有些地方需要通過手動方式才能啟動,看下面幾個常見情景:

# cmd 中浮點數沒有被緩存
a = 1.0
b = 1.0
a is b # False

# cmd 中並非全部的字符串都會采用intern機制; 僅 包括下劃線、數字、字母的字符串才會被 intern--類標識符
s1="hello"
s2="hello"
s1 is s2 # True

# 如果有空格,默認不啟用intern機制
s1="hell o"
s2="hell o"
s1 is s2 # False

s1 = "hell!*o"
s2 = "hell!*o"
print(s1 is s2) # False

# 如果一個字符串長度超過20個字符,不啟動intern機制 -- 看網上很多都是這麼寫的, 不超過二十個就為真,但是我在自己 3.7/8.5 版本上試瞭一下,發現好像沒有限制,不知道是 Python 更新瞭,還是什麼問題……
s1 = "a" * 20
s2 = "a" * 20
s1 is s2 # True

s1 = "a" * 21
s2 = "a" * 21
s1 is s2 # True

s1 = "ab" * 10
s2 = "ab" * 10
s1 is s2 # True

s1 = "ab" * 11
s2 = "ab" * 11
s1 is s2 # True

# 'kz' + 'c' 編譯時已經變成 'kzc',而 s1 + 'c' 中 s1 是變量, 會在運行時進行拼接,所以沒有被intern?
'kz' + 'c' is 'kzc' # True

s1 = 'kz'
s2 = 'kzc'
s1+'c' is 'kzc' # False

# pycharm 等編輯器中,隻要是同一個字符串,都為 True,並不用是下劃線、數字、字母的字符串
s1 = "hell o"
s2 = "hell o"
print(s1 is s2) # True

s1 = "hell!*o"
s2 = "hell!*o"
print(s1 is s2) # True

s1 = "a" * 20
s2 = "a" * 20
print(s1 is s2) # True

s1 = "a" * 21
s2 = "a" * 21
print(s1 is s2) # True

s1 = "ab" * 10
s2 = "ab" * 10
print(s1 is s2) # True

s1 = "ab" * 11
s2 = "ab" * 11
print(s1 is s2) # True

'kz' + 'c' is 'kzc' # True

s1 = 'kz'
s2 = 'kzc'
s1+'c' is 'kzc' # False

# 編輯器中,float 也被緩存瞭
a = 1.0
b = 1.0
a is b

以上就是詳解Python 小數據池和代碼塊緩存機制的詳細內容,更多關於Python 小數據池和代碼塊緩存機制的資料請關註WalkonNet其它相關文章!