Python - is vs == 的差異詳解

在 Python 程式碼中,我們經常需要比較兩個物件。這時你會面臨兩個選擇:== 運算子與 is 運算子。對於初學者來說,這兩者看起來似乎在做同樣的事情,但在 Python 的底層機制中,它們代表了完全不同的概念:「相等性 (Equality)」「同一性 (Identity)」

簡單來說:

  • == 比較的是物件的內容(值)
  • is 比較的是物件的記憶體位址(身分)

== (Equality):值是否相等?

當你使用 == 時,你是在問:「這兩個物件裡面的資料長得一樣嗎?」

Python 在執行 == 比較時,實際上是呼叫了物件的 __eq__() 魔術方法。只要兩個物件的內容一致,結果就是 True

1
2
3
4
list1 = [1, 2, 3]
list2 = [1, 2, 3]

print(list1 == list2) # True,因為內容完全一樣

is (Identity):是不是同一個物件?

當你使用 is 時,你是在問:「這兩個變數是不是指向記憶體中同一個位址?」

你可以把 is 想成是比較兩個物件的 id()。如果 id(a) == id(b),那麼 a is b 就會是 True

1
2
3
4
list1 = [1, 2, 3]
list2 = [1, 2, 3]

print(list1 is list2) # False,因為它們是存放在不同位置的兩個獨立列表

如果我們讓一個變數指向另一個變數:

1
2
list3 = list1
print(list1 is list3) # True,因為 list3 只是 list1 的別名,指向同一個位址

為什麼 None 總是用 is

在 Python 的慣例(PEP 8)中,檢查一個變數是否為 None 時,永遠應該使用 is None 而非 == None

1
2
if x is None:
pass

為什麼?

  1. 效能: is 只是比較指標,速度極快。
  2. 安全: None 在 Python 中是一個單例 (Singleton) 物件。== 可能會因為物件自定義了 __eq__ 方法而回傳錯誤的結果,但 is 保證了你是在跟那個唯一的 None 物件做比較。

Python 的小把戲:整數與字串的 Interning

有時候,你會發現一些「違背直覺」的現象:

1
2
3
a = 10
b = 10
print(a is b) # 竟然是 True?

按照前面的邏輯,ab 應該是獨立的物件,為什麼 is 會回傳 True

這是因為 Python 為了優化效能,會進行 Interning (駐留) 機制。Python 會預先建立好一些常用的物件並重複使用它們(延伸閱讀:Python - 10 is 10 vs 500 is 500):

  • 小整數快取: Python 會快取 -5 到 256 之間的所有整數。當你賦值 10 時,其實都是指向同一個全域整數物件。
  • 字串駐留: 短字串或符合標識符規則的字串也常被 Interning。

字串也有類似的 Interning 機制。符合標識符規則(只含字母、數字、底線)的短字串通常會被自動駐留:

1
2
3
4
5
6
7
a = "hello"
b = "hello"
print(a is b) # 通常是 True(符合標識符規則,被自動駐留)

a = "hello world"
b = "hello world"
print(a is b) # 通常是 False(含空格,不符合標識符規則)

如果你需要強制讓字串被駐留,可以使用 sys.intern()

1
2
3
4
5
import sys

a = sys.intern("hello world")
b = sys.intern("hello world")
print(a is b) # True,因為被強制駐留到同一個物件

注意: 在某些 IDE 或編譯後的腳本中,編譯器可能會進行更激進的優化,讓 1000 is 1000 也變成 True。因此,絕對不要依賴 is 來比較數值或字串

總結

特性== (Equality)is (Identity)
比較對象內容、數值記憶體位址 (id)
底層方法__eq__()比較 id()
使用時機判斷內容是否相等判斷是否為 None 或同一物件
常見陷阱無法區分不同記憶體位置易受 Interning 機制誤導

記住一個簡單的原則:除非你想檢查「是不是同一個人」,否則請一律使用 == 而檢查 None 則是唯一你應該養成習慣使用 is 的地方。

也許你也會想看看