Python 繼承與多型

Python 繼承(Python Inheritance)

這篇 Python 繼承教學將帶你認識類別之間的「親子關係」:子類別自動擁有父類別的屬性與方法,再加上自己特有的部分。共通邏輯寫一次放父類別,差異部分各自實作——這就是繼承解決的問題。

基本繼承

類別名稱後面加 (父類別) 就是繼承:

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

    def eat(self):
        print(f"{self.name} 在吃東西")

class Dog(Animal):              # Dog 繼承 Animal
    def bark(self):
        print(f"{self.name}:汪汪!")

dog = Dog("小黑")
dog.eat()      # 繼承自 Animal,不用重寫
dog.bark()     # Dog 自己的方法

執行輸出:

小黑 在吃東西
小黑:汪汪!

方法覆寫(Override)

子類別定義同名方法,就會「蓋掉」父類別的版本:

class Animal:
    def speak(self):
        print("動物發出聲音")

class Dog(Animal):
    def speak(self):            # 覆寫父類別的 speak
        print("汪汪!")

class Cat(Animal):
    def speak(self):
        print("喵~")

Dog().speak()
Cat().speak()

執行輸出:

汪汪!
喵~

super():呼叫父類別的方法

子類別擴充 __init__ 時,先用 super().__init__() 把父類別的初始化做完,再加自己的:

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)     # 讓父類別處理 name
        self.breed = breed         # 自己只處理新增的部分

dog = Dog("小黑", "柴犬")
print(dog.name, dog.breed)

執行輸出:

小黑 柴犬

多型(Polymorphism)

多型=「同一個操作,不同型別各自有不同表現」。好處是呼叫端不用管物件是哪個子類別:

class Dog:
    def speak(self):
        return "汪汪!"

class Cat:
    def speak(self):
        return "喵~"

animals = [Dog(), Cat(), Dog()]

for a in animals:
    print(a.speak())     # 不用判斷型別,各自表現

執行輸出:

汪汪!
喵~
汪汪!

注意 Python 的多型不要求共同父類別——只要有同名方法就能這樣呼叫(俗稱鴨子型別:走起來像鴨子、叫起來像鴨子,那就是鴨子)。

isinstance():檢查型別

需要判斷物件型別時用 isinstance(),繼承關係也算:

class Animal: pass
class Dog(Animal): pass

dog = Dog()
print(isinstance(dog, Dog))
print(isinstance(dog, Animal))   # Dog 是 Animal 的子類別,也算
print(isinstance(dog, str))

執行輸出:

True
True
False

常見錯誤

1. 子類別 __init__ 忘了呼叫 super().__init__()

子類別定義了自己的 __init__ 就會完全取代父類別的,父類別設定的屬性全部消失,之後存取就報 AttributeError。記住:覆寫 __init__ 時第一件事就是 super().__init__(...)

2. 繼承層級太深

A 繼承 B 繼承 C 繼承 D⋯⋯追一個方法要翻好幾層。實務原則:繼承兩層內解決,更複雜的關係改用「組合」(物件裡放物件)。

3. 用 type() 判斷型別而非 isinstance()

type(dog) == Animal 對子類別會是 False,破壞多型的彈性。判斷型別一律用 isinstance()

總結

class Dog(Animal) 繼承、同名方法覆寫、super() 呼叫父類別版本(覆寫 __init__ 必加);多型讓呼叫端不用管實際型別;型別判斷用 isinstance()。下一篇認識讓自訂類別融入 Python 語法的特殊方法(Magic Methods)。

延伸閱讀