本文 AI 產出,尚未審核

Python 物件導向程式設計(OOP)

主題:super()


簡介

在 Python 的類別繼承(inheritance)中,子類別常需要呼叫父類別的建構子或其他方法,以保留父類別已實作的行為,同時再加入自己的擴充。
如果直接以 ParentClass.__init__(self, ...) 的方式呼叫,會產生 硬編碼(hard‑coding)的問題:當類別層級變動或多重繼承(multiple inheritance)時,程式碼很容易出錯或難以維護。

super() 正是為了解決這類問題而設計的 內建函式。它會自動搜尋 方法解析順序(Method Resolution Order, MRO),找出正確的父類別,並以正確的方式呼叫對應的方法。掌握 super() 的用法,能讓你的類別設計更具彈性、可讀性更佳,也更符合 Pythonic 的編程風格。


核心概念

1. 為什麼需要 super()

  • 避免硬編碼:不必寫死父類別名稱,子類別改名或改層級時不必修改呼叫程式碼。
  • 支援多重繼承:在菱形繼承(diamond inheritance)結構中,super() 會依照 MRO 只呼叫一次每個父類別的方法,避免重複執行。
  • 提升可維護性:使用 super() 的類別更易於重構與測試。

2. super() 的基本語法

super().method_name(args)
  • Python 3 允許省略 super() 的兩個參數(typeobj),由解譯器自動推斷。
  • Python 2 必須寫成 super(CurrentClass, self).method_name(args)

3. 方法解析順序(MRO)

每個類別都有一個 __mro__ 屬性,儲存類別的搜尋順序。例如:

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.__mro__)   # (<class '__main__.D'>, <class '__main__.B'>,
                   #  <class '__main__.C'>, <class '__main__.A'>,
                   #  <class 'object'>)

super() 會依照這個順序尋找下一個實作的方法。


程式碼範例

以下提供 5 個實用範例,從最簡單的單一繼承到複雜的多重繼承,說明 super() 的正確使用方式與注意事項。

範例 1️⃣:單一繼承中的建構子呼叫

class Animal:
    def __init__(self, name):
        self.name = name
        print(f"[Animal] 初始化 {self.name}")

class Dog(Animal):
    def __init__(self, name, breed):
        # 使用 super() 呼叫父類別的 __init__
        super().__init__(name)
        self.breed = breed
        print(f"[Dog] 品種是 {self.breed}")

# 測試
d = Dog("Buddy", "Golden Retriever")

執行結果

[Animal] 初始化 Buddy
[Dog] 品種是 Golden Retriever

重點super().__init__(name)Dog 不必知道父類別的名稱是 Animal,若日後改名或改層級,程式仍能正常運作。


範例 2️⃣:多層次繼承(Grandparent → Parent → Child)

class Vehicle:
    def start(self):
        print("Vehicle 啟動")

class Car(Vehicle):
    def start(self):
        print("Car 準備啟動")
        super().start()          # 呼叫 Vehicle.start

class SportsCar(Car):
    def start(self):
        print("SportsCar 引擎點火")
        super().start()          # 呼叫 Car.start

# 測試
s = SportsCar()
s.start()

執行結果

SportsCar 引擎點火
Car 準備啟動
Vehicle 啟動

說明:每層都只負責自己的部份,super() 依照 MRO 依序向上呼叫,讓程式碼保持 單一職責(Single Responsibility)。


範例 3️⃣:多重繼承(菱形繼承)與 super()

class A:
    def greet(self):
        print("Hello from A")

class B(A):
    def greet(self):
        print("Hello from B")
        super().greet()

class C(A):
    def greet(self):
        print("Hello from C")
        super().greet()

class D(B, C):
    def greet(self):
        print("Hello from D")
        super().greet()

# 測試
d = D()
d.greet()
print("MRO:", D.__mro__)

執行結果

Hello from D
Hello from B
Hello from C
Hello from A
MRO: (<class '__main__.D'>, <class '__main__.B'>,
      <class '__main__.C'>, <class '__main__.A'>,
      <class 'object'>)

關鍵:即使 ABC 同時繼承,super() 只會呼叫一次 A.greet,避免重複執行。


範例 4️⃣:在非建構子(ordinary method)中使用 super()

class LoggerMixin:
    def log(self, message):
        print(f"[LOG] {message}")

class Service(LoggerMixin):
    def process(self, data):
        self.log(f"Processing {data}")
        # 其他業務邏輯...
        print("處理完成")

svc = Service()
svc.process("使用者資料")

執行結果

[LOG] Processing 使用者資料
處理完成

說明super() 不僅限於 __init__,任何方法皆可使用,以 混入(Mixin) 方式擴充功能,保持程式碼的 可重用(reusability)。


範例 5️⃣:super() 搭配 __new__(自訂物件建立)

class MyInt(int):
    def __new__(cls, value):
        print("在 __new__ 中自訂建立過程")
        # 必須使用 super().__new__ 產生真正的 int 物件
        obj = super().__new__(cls, value)
        return obj

    def __init__(self, value):
        # __init__ 在此僅作為示範,int 本身不需要額外初始化
        print(f"在 __init__ 中接收到 {value}")

# 測試
num = MyInt(42)
print(num, type(num))

執行結果

在 __new__ 中自訂建立過程
在 __init__ 中接收到 42
42 <class '__main__.MyInt'>

要點__new__ 必須返回一個實例,若忘記呼叫 super().__new__,將無法正確建立基底類別的內建型別。


常見陷阱與最佳實踐

常見問題 說明 解決方式
忘記加 super() 直接呼叫父類別方法會硬編碼,且在多重繼承時會重複呼叫。 使用 super().method(),讓 MRO 決定呼叫目標。
__init__ 內未傳遞所有參數 父類別需要的參數若未傳遞,會拋出 TypeError 確認 super().__init__(**kwargs) 或使用 關鍵字參數 透過 *args, **kwargs 轉遞。
__new__ 中忘記回傳物件 __new__ 必須回傳一個實例,否則會得到 None obj = super().__new__(cls, *args, **kwargs),最後 return obj
混用舊式 super(Class, self) 與新式 super() 兩者行為相同,但寫法不一致會降低可讀性。 統一使用 Python 3 的簡寫 super()
在靜態方法(@staticmethod)中使用 super() 靜態方法沒有 self,無法取得 MRO。 若需要呼叫父類別的靜態方法,直接使用類別名稱;或改為 類別方法(@classmethod),再使用 super()

最佳實踐

  1. 盡量在所有可覆寫的方法中使用 super(),即使目前只有單一繼承,未來擴充時也不會出問題。
  2. 使用 *args, **kwargs 轉遞參數,保持子類別與父類別的介面彈性。
  3. 在多重繼承時,確保每個父類別都使用 super(),如此才能形成「協同」的呼叫鏈。
  4. 避免在 __init__ 內執行副作用(例如 I/O),因為 super() 可能會被多次呼叫(如在菱形繼承中)。將副作用搬到獨立方法或屬性初始化中。
  5. 對於混入(Mixin)類別,只實作單一職責的方法,並使用 super() 讓其他混入也能參與呼叫鏈。

實際應用場景

1. Web 框架的請求處理管線(Middleware)

在 Django、Flask 等框架中,開發者常會自訂 中間件(middleware) 來前置或後置處理請求。若每個中間件都使用 super() 呼叫下一層,就能形成 可插拔 的處理鏈,且不必擔心中間件的排列順序。

class BaseMiddleware:
    def process_request(self, request):
        # 預設什麼都不做
        pass

class AuthMiddleware(BaseMiddleware):
    def process_request(self, request):
        print("驗證使用者")
        super().process_request(request)

class LoggingMiddleware(BaseMiddleware):
    def process_request(self, request):
        print("記錄請求")
        super().process_request(request)

# 組合
class AppMiddleware(LoggingMiddleware, AuthMiddleware):
    pass

req = {}
AppMiddleware().process_request(req)

輸出

記錄請求
驗證使用者

2. GUI 元件的多層次初始化

在 PyQt、Tkinter 這類 GUI 框架,子類別往往需要先呼叫父類別的建構子以建立底層視窗資源,再加入自訂的佈局或事件。使用 super() 能保證 跨平台未來框架升級 時的相容性。

3. 資料模型的多重繼承(Mixin)

常見的 ORM(如 SQLAlchemy)會提供 TimestampMixinSoftDeleteMixin 等混入類別。透過 super(),每個 mixin 都能在 __init__ 中自動設定欄位,而不必擔心呼叫順序。

class TimestampMixin:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.created_at = datetime.now()
        self.updated_at = datetime.now()

class SoftDeleteMixin:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.is_deleted = False

class User(TimestampMixin, SoftDeleteMixin, BaseModel):
    pass

4. 測試雙倍(Test Double)與 Mock

在單元測試中,常會建立 子類別 來覆寫部份行為,同時仍希望保留原本的邏輯。super() 讓測試子類別可以在需要時呼叫真實實作,提升測試的真實度。


總結

super() 是 Python 物件導向程式設計中不可或缺的工具,它能:

  • 自動依照 MRO 找到正確的父類別,避免硬編碼。
  • 支援多重繼承,確保每個父類別的行為只被呼叫一次。
  • 提升程式碼彈性與可維護性,讓未來的類別重構更安全。

在實務開發中,從 建構子普通方法Mixin、到 __new__,只要遵循「每層都使用 super()」的原則,就能建立協同可擴充的類別層次結構。記得配合 *args, **kwargs 轉遞參數、在多重繼承時保持每個父類別都實作 super(),並留意靜態方法與特殊方法的限制,這樣就能在各種應用場景(Web 框架、GUI、ORM、測試)中自如運用 super(),寫出更 Pythonic、更具可讀性的程式碼。祝你在 Python OOP 的旅程中玩得開心、寫得順手!