Python 物件導向進階:繼承、多型與 MRO (第 16 章)

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

在掌握了類別與物件的基礎後,我們要進一步探討 OOP 的三大支柱:封裝 (Encapsulation)、繼承 (Inheritance) 與 多型 (Polymorphism)。本章將聚焦於繼承與多型,這是建構大型、可擴充系統的基石。

1. 繼承 (Inheritance):站在巨人的肩膀上

繼承允許我們定義一個「子類別」(Child Class),它會自動擁有「父類別」(Parent Class) 的所有屬性與方法。

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "..."

# Dog 繼承自 Animal
class Dog(Animal):
    def speak(self):
        return "Woof!"

# Cat 繼承自 Animal
class Cat(Animal):
    def speak(self):
        return "Meow!"

d = Dog("Buddy")
print(f"{d.name} says {d.speak()}") # Buddy says Woof!

在這個例子中,DogCat 不需要重新寫 __init__,因為它們繼承了 Animal 的建構子。

2. super():呼叫父類別

當我們在子類別「透過覆寫 (Override)」修改了父類別的方法,但又想保留父類別原有的邏輯時,就要用 super()

class Bird(Animal):
    def __init__(self, name, can_fly):
        # 呼叫父類別的 __init__ 來初始化 name
        super().__init__(name)
        self.can_fly = can_fly

    def speak(self):
        # 先執行父類別的 speak (雖然這裡只回傳 "...")
        original_sound = super().speak()
        return f"{original_sound} Tweet!"

b = Bird("Tweety", True)
print(b.name, b.can_fly)

super() 讓程式碼更具彈性,就算未來父類別改名或邏輯變動,子類別通常不需要修改。

3. 多重繼承與 MRO (Method Resolution Order)

Python 支援 多重繼承,也就是一個類別可以有多個父類別。但如果兩個父類別都有同名的方法,Python 該呼叫哪一個?

這取決於 MRO (方法解析順序)。Python 使用 C3 Linearization 演算法來決定繼承順序。

class A:
    def process(self):
        print("A process")

class B(A):
    def process(self):
        print("B process")

class C(A):
    def process(self):
        print("C process")

# D 同時繼承 B 和 C
class D(B, C):
    pass

d = D()
d.process() 
# 輸出: B process
# 因為 D 繼承列表是 (B, C),Python 會先找 B

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

理解 MRO 對於使用像是 Django Mixin 這類的設計模式至關重要。

4. 多型 (Polymorphism):「鴨子型別」

Python 是動態語言,我們不須像 Java 或 C++ 那樣明確定義介面。Python 奉行 鴨子型別 (Duck Typing)

「如果它走起來像鴨子,叫起來像鴨子,那它就是鴨子。」

def animal_sound(animal):
    # 不需要檢查 animal 是不是 Animal 的子類別
    # 只要它有 speak() 方法就可以
    print(animal.speak())

class Car:
    def speak(self):
        return "Vroom!"

animal_sound(Dog("Doge"))  # Woof!
animal_sound(Car())        # Vroom! (雖然車不是動物,但也能跑!)

這賦予了 Python 極大的靈活性。

5. 抽象基礎類別 (ABC)

雖然鴨子型別很方便,但有時我們希望強制子類別必須實作某些方法。這時可以使用 abc 模組。

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    # 如果不實作 area,實例化時會噴錯
    def area(self):
        return 3.14 * self.radius ** 2

# s = Shape() # TypeError: Can't instantiate abstract class
c = Circle(5)
print(c.area())

6. 總結

繼承與多型是讓程式碼模組化、可維護的關鍵。

本章重點回顧:

  • Inheritance: 子類別繼承父類別的特性。
  • Override: 子類別可以覆寫父類別的方法。
  • super(): 用於呼叫父類別的方法,特別是在 __init__ 中。
  • MRO: 多重繼承時,Python 尋找方法的順序 (由左至右,由下而上)。
  • Duck Typing: Python 的多型不需要繼承特定介面,只要方法存在即可。

下一章,我們將揭開 Python 物件的神秘面紗,探討 魔術方法 (Magic Methods),讓你的自訂物件能像內建型別一樣使用 +, -, len() 等運算子!


延伸閱讀

LATEST POST
TAG