Python 特殊方法(Magic Methods)
這篇教學將帶你認識前後雙底線的特殊方法(magic methods/dunder methods)。為什麼 len("abc")、a == b、print(obj) 對內建型別都有合理行為?因為它們背後各自呼叫 __len__、__eq__、__str__。在自己的類別實作這些方法,你的物件就能像內建型別一樣自然好用。
| 特殊方法 | 觸發時機 |
|---|---|
__init__ | 建立物件時(前篇學過) |
__str__ | print(obj)、str(obj) |
__repr__ | 直接在 REPL 顯示、repr(obj)、容器內顯示 |
__eq__ | obj1 == obj2 |
__len__ | len(obj) |
__add__ | obj1 + obj2 |
__str__:讓 print 印出人話
沒定義 __str__ 時,print 出來是看不懂的記憶體位址:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 5)
print(p)
執行輸出(位址每次不同):
<__main__.Point object at 0x7f3c2c1d5a90>
加上 __str__:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Point({self.x}, {self.y})"
p = Point(3, 5)
print(p)
執行輸出:
Point(3, 5)
__repr__:給開發者看的版本
__str__ 給使用者看、__repr__ 給開發者除錯看(慣例上是「能重建物件的程式碼樣貌」)。List 裡的物件顯示用的是 __repr__,這是常見的疑惑點:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
points = [Point(1, 2), Point(3, 4)]
print(points) # 容器內用 __repr__
執行輸出:
[Point(1, 2), Point(3, 4)]
實務建議:至少定義 __repr__——沒定義 __str__ 時,print 會退而使用 __repr__,一個方法兩種場合都顧到。
__eq__:自訂「相等」的意義
預設的 == 比的是「是不是同一個物件」,不是內容:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(3, 5)
p2 = Point(3, 5)
print(p1 == p2) # 內容相同,但預設比較身分
執行輸出:
False
定義 __eq__ 改成比內容:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
p1 = Point(3, 5)
p2 = Point(3, 5)
print(p1 == p2)
執行輸出:
True
__len__ 與 __add__
class Playlist:
def __init__(self, songs):
self.songs = songs
def __len__(self):
return len(self.songs)
def __add__(self, other):
return Playlist(self.songs + other.songs)
a = Playlist(["歌1", "歌2"])
b = Playlist(["歌3"])
merged = a + b # 觸發 __add__
print(len(merged)) # 觸發 __len__
print(merged.songs)
執行輸出:
3
['歌1', '歌2', '歌3']
常見錯誤
1. print 出 <object at 0x…>
忘了定義 __str__/__repr__。看到這串就是提醒你補上。
2. __str__ 忘記 return 字串
__str__ 裡用 print 而不是 return,會報 TypeError: __str__ returned non-string。它要「回傳」字串,不是自己印。
3. 定義了 __eq__ 後物件不能放進 set
自訂 __eq__ 會讓物件失去預設的雜湊值(unhashable),放進 set 或當 dict key 會報錯。需要的話再實作 __hash__,或改用下一篇的 dataclass 讓它自動處理。
總結
特殊方法讓自訂類別接上 Python 內建語法:__repr__ 必備(print 與容器顯示都靠它)、__str__ 給使用者、__eq__ 把比較改成比內容、__len__ 與 __add__ 支援 len() 與 +。覺得這些樣板很繁瑣?下一篇的 dataclass 幫你全部自動生成。