Python 魔術方法:Dunder Methods 的奧義 (第 17 章)

站主自己的課程,請大家支持
揭秘站長的架站心法:如何利用 Hugo × AI 打造高質感個人品牌網站? 揭秘站長的架站心法:如何利用 Hugo × AI 打造高質感個人品牌網站?
  • Post by
  • Dec 31, 2023
post-thumb

在 Python 中,你經常會看到以雙底線 (Double Underscore) 開頭和結尾的方法,例如 __init__。因為唸起來像 “Double Under”,所以開發者常暱稱它們為 Dunder Methods,或者更帥氣的稱呼——魔術方法 (Magic Methods)

這些方法會在特定的情況下被 Python 直譯器自動呼叫。透過實作它們,我們可以讓自訂的物件表現得像 Python 的內建型別 (如 List, Int, String) 一樣自然。

1. 字串表示:__str____repr__

當我們 print() 一個物件時,通常會看到像 <__main__.Car object at 0x10a2b...> 這樣看不懂的訊息。我們可以透過覆寫這兩個方法來改善。

  • __str__: 給使用者看的,力求可讀性 (Readability)。
  • __repr__: 給開發者看的,力求明確性 (Unambiguous),最好能用來重新產生該物件。
class Car:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def __str__(self):
        return f"{self.brand} {self.model}"

    def __repr__(self):
        return f"Car(brand='{self.brand}', model='{self.model}')"

my_car = Car("Tesla", "Model Y")

print(str(my_car))  # Tesla Model Y (呼叫 __str__)
print(repr(my_car)) # Car(brand='Tesla', model='Model Y') (呼叫 __repr__)

最佳實踐:至少要寫 __repr__。如果沒有寫 __str__,Python 會自動退而求其次呼叫 __repr__

2. 運算子多載 (Operator Overloading):__add__

想要讓兩個物件像數字一樣相加嗎?實作 __add__ 即可。

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        # 回傳一個新的 Vector 物件
        return Vector(self.x + other.x, self.y + other.y)

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(2, 4)
v2 = Vector(3, 1)
v3 = v1 + v2

print(v3) # Vector(5, 5)

常用運算子對應表:

  • +: __add__
  • -: __sub__
  • *: __mul__
  • ==: __eq__
  • <: __lt__

3. 容器行為:__len____getitem__

想讓你的物件能用 len() 測長度,或者像 List 一樣用 obj[0] 取值嗎?

class Deck:
    def __init__(self):
        self.cards = ['A', '2', '3', '4', '5']

    def __len__(self):
        return len(self.cards)

    def __getitem__(self, position):
        return self.cards[position]

deck = Deck()

print(len(deck)) # 5
print(deck[0])   # A
print(deck[-1])  # 5 (因為 list 支援負索引,這裡直接轉發給 list)

只要實作了 __getitem__,物件甚至自動支援 for 迴圈迭代!

4. 可呼叫物件:__call__

讓物件像函數一樣被呼叫。

class Multiplier:
    def __init__(self, factor):
        self.factor = factor

    def __call__(self, x):
        return x * self.factor

double = Multiplier(2)
print(double(10)) # 20 (這看起來像函數呼叫,其實是物件!)

這在編寫裝飾器或需要保存狀態的回呼函數 (Callback) 時非常有用。

5. 資源管理:__enter____exit__

這是 with 語句背後的魔法,我們將在第 19 章 (Context Managers) 詳細介紹。

6. 總結

魔術方法是讓 Python 程式碼變得優雅、直觀的關鍵。但請記得:能力越強,責任越大。不要為了炫技而過度使用運算子多載 (例如把 + 定義成「相減」),這會讓維護你程式碼的人崩潰。

本章重點回顧:

  • init: 建構子。
  • str / repr: 字串表示法。
  • add / eq: 運算子行為定義。
  • len / getitem: 讓物件像容器一樣。
  • call: 讓物件像函數一樣被呼叫。

下一章,我們將學習 Python 類別設計中的進階技巧,包括 @property (屬性封裝)、@classmethod@staticmethod,讓你的類別介面更乾淨安全!


延伸閱讀

LATEST POST
TAG