Python 中 is 和 == 的區別

鹹魚運維雜談發表於2023-03-29

 

 is 和 == 的區別

相信學過 Python 小夥伴們都知道 is 和 == 都是用來比較 Python 物件的,但是區別就是

  • is 比較需要物件的值和記憶體地址都相等
  • == 比較只需要物件的值相等就行了

 

我們來看一個例子

 

 

 

我們可以看到,time 模組的 time() 方法用於獲取當前時間,所以 t1 和 t2 的值都是一樣的

 

== 用來判斷 t1 和 t2 的值是否相等,所以返回 True

 

雖然 t1 和 t2 的值相等,但它們是兩個不同的物件(每次呼叫 time() 都返回不同的物件),所以t1 is t2返回 False

 

那麼,如何判斷兩個物件是否相同呢?

 

答:判斷兩個物件的記憶體地址。如果記憶體地址相同,說明兩個物件使用的是同一塊記憶體,當然就是同一個物件了

 

我們來看下 t1 和 t2 的記憶體地址

 

 

 

可以看到它們兩個的記憶體地址是不一樣的

 

 

小整數池&快取機制

 但是有小夥伴可能會遇到下面的這種情況

 

咦?怎麼 a is b 結果是 True?這應該是兩個不同的物件啊

 

這其實是因為小整數池

 

python 中經常使用的一些數值定義為小整數池,小整數池的範圍是[-5,256]

 

python 對這些數值已經提前建立好了記憶體空間,即使多次重新定義也不會再重新開闢新的空間,但是小整數池外的數值在重新定義時都會再次開闢新的空間

 

所以對於小整數池中的數,記憶體地址一定是相同的,小整數池中外的數,記憶體地址是不同的

 

好,那這次我用小整數池之外的數

 

 

 

 

 

?玩我是吧,說好的小整數池中外的數,記憶體地址是不同的,那上面的程式碼結果怎麼跟說的不一樣

 

上面的程式碼我是在 IDE 環境下面敲的,我們試著在互動模式下敲

 

 

可以看到,在互動模式下,小整數池外的數記憶體地址不相同,這是為什麼呢?

 

先說結論:這是因為 Python 的快取機制,所以在 IDE 環境或者指令碼模式下同一個整數被多個變數引用不會開闢新的記憶體空間

 

 

Python 快取機制

 

  • Python 直譯器啟動時會先從記憶體空間中開闢出來一小部分,用於儲存高頻使用的資料(不可變資料型別),這樣可以大大減少高頻使用的資料物件建立時申請記憶體和銷燬時撤銷記憶體的開銷

  • 在同一程式碼塊下,不可變資料型別的物件(數字,字串,元祖)被多個變數引用,不會重複開闢記憶體空間

 

由上面得知,只有不可變的資料型別(字串、元祖、基礎資料型別)如果被多個變數引用,是不會重複開闢記憶體空間,但可變資料型別(列表、字典、集合)就除外

 

  • 可變資料型別

我們來看看

 

 

 

 

 

在互動模式下結果也是如此

 

  • 不可變資料型別

1、小整數池裡的數

我們來看下互動模式下的不可變資料型別的快取機制

 

 

可以看到,Python 中整數範圍 [-5, 256] 中的數為固定快取,只要是使用到該範圍內的數字,不管是直接賦值還是表示式計算得到的,都會使用固定快取中的資料

 

2、非小整數池裡的數

對於非小整數池裡的數,在 IDE 環境下會使用到快取,即多個變數引用同一個資料,不會開闢新的記憶體空間

 

對於非小整數池裡的數,在互動模式下,除非同時賦值或者在同一個局域程式碼塊裡面賦值,否則不會使用快取機制

 

 

 

 

intern 機制

我們知道,由於 Python 的快取機制:

  • 不可變的資料型別(字串、元祖、基礎資料型別)如果被多個變數引用,是不會重複開闢記憶體空間

  • 但可變資料型別(列表、字典、集合)被多個變數引用就會開闢新的記憶體空間

  • 對於小整數池裡的整數,被多個變數引用,不會重複開闢記憶體空間

 

但是到目前為止我們知道:在互動模式下,除了特殊情況(同時賦值、同一局域程式碼塊內賦值)以及小整數池之外,所有資料在被多個變數引用時都會開闢新的記憶體空間

 

其實還有一種特殊情況,我們來看這麼一個例子

 

看著輸出的結果,再跟剛剛所學到的知識做一下對比,是不是發現有不對勁的地方

 

互動模式下,多個變數引用字串(不可變資料型別)應該是開闢新的記憶體空間啊,為啥上面的例子沒有開闢

 

intern機制

字串型別作為Python中最常用的資料型別之一,Python 為了提高字串使用的效率和使用效能,使用了 intern(字串駐留)的技術來提高字串效率

 

即值同樣的字串物件僅僅會儲存一份,放在一個字串儲蓄池中,是共用的,有新的變數引用同樣的字串的時候,不會開闢新的記憶體空間,而是引用這個共用的字串

 

  • 原理

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

 

下面是虛擬碼

 

1、在互動模式下,只包含字母數字下劃線的字串才會觸發 intern 機制

 

 

 

 

2、在 IDE 環境或者指令碼模式下,只要長度不超過20(長度限制),即使使用特殊字元也會觸發 intern 機制

 

 

 

PS:我在寫這篇文章的時候用的是 python 3.9,發現沒有長度限制了,都會觸發 intern 機制

 

 


感謝閱讀,喜歡作者就動動小手[一鍵三連],這是我寫作最大的動力

 

相關文章