前言
本文除"總結"外,其余均為認識程序;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 = 'janes@!#*ewq'
s2 = 'janes@!#*ewq'
print(s1 is s2) # True
a1 = 'janes45613256132!@#$%#^%@$%' * 1
b1 = 'janes45613256132!@#$%#^%@$%' * 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
參考:
https://www.zhihu.com/question/29945705 python里的怪問題
https://www.pianshen.com/article/9128116263/ python-小資料池,代碼塊深入剖析
https://www.dazhuanlan.com/2020/01/16/5e1f70e908538/ cpython 中的 string interning
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/273124.html
標籤:Python
