Python 繼承 (Inheritance)
繼承讓子類別可以繼承父類別的屬性和方法,實現程式碼重用和擴展。
基本繼承
# 父類別(基類別)
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} makes a sound")
# 子類別(衍生類別)
class Dog(Animal):
def bark(self):
print(f"{self.name} says: Woof!")
# 使用
dog = Dog("Buddy")
dog.speak() # Buddy makes a sound(繼承自 Animal)
dog.bark() # Buddy says: Woof!(Dog 自己的方法)
方法覆寫 (Override)
子類別可以覆寫父類別的方法:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} makes a sound")
class Dog(Animal):
def speak(self):
print(f"{self.name} says: Woof!")
class Cat(Animal):
def speak(self):
print(f"{self.name} says: Meow!")
dog = Dog("Buddy")
cat = Cat("Whiskers")
dog.speak() # Buddy says: Woof!
cat.speak() # Whiskers says: Meow!
super() 函數
使用 super() 呼叫父類別的方法:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
return f"{self.name}, {self.age} years old"
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # 呼叫父類別的 __init__
self.breed = breed
def info(self):
base_info = super().info() # 呼叫父類別的 info
return f"{base_info}, breed: {self.breed}"
dog = Dog("Buddy", 3, "Labrador")
print(dog.info()) # Buddy, 3 years old, breed: Labrador
isinstance() 和 issubclass()
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
# isinstance 檢查物件是否為某類別的實例
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True(Dog 繼承自 Animal)
print(isinstance(dog, object)) # True(所有類別都繼承自 object)
# issubclass 檢查類別是否為另一類別的子類別
print(issubclass(Dog, Animal)) # True
print(issubclass(Dog, object)) # True
print(issubclass(Animal, Dog)) # False
多重繼承
Python 支援多重繼承,一個類別可以繼承多個父類別:
class Flyable:
def fly(self):
print("Flying...")
class Swimmable:
def swim(self):
print("Swimming...")
class Duck(Flyable, Swimmable):
def quack(self):
print("Quack!")
duck = Duck()
duck.fly() # Flying...
duck.swim() # Swimming...
duck.quack() # Quack!
方法解析順序 (MRO)
當一個類別繼承自多個父類別時,Python 需要一個規則來決定當呼叫某個方法時,應該優先使用哪一個父類別的版本。這個規則稱為 方法解析順序 (Method Resolution Order, MRO)。
Python 使用 C3 線性化演算法 (C3 Linearization) 來計算 MRO。簡單來說,它的搜尋順序原則如下:
- 先搜尋自己 (子類別)。
- 依照繼承列表由左至右搜尋父類別。
- 如果父類別還有父類別,則深度優先,但會保持由左至右的順序,且確保每個類別只被搜尋一次(對於鑽石繼承結構特別重要)。
我們可以使用 類別名稱.mro() 方法或是 類別名稱.__mro__ 屬性來查看實際的搜尋順序。
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
d = D()
d.method() # B(根據 MRO)
# 查看 MRO
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
使用 super() 處理多重繼承
在單一繼承中,super() 通常指的就是「父類別」。但在多重繼承中,super() 的行為更加強大且重要:它指的不是「父類別」,而是 MRO 順序中的下一個類別。
確實使用 super() 對於多重繼承非常關鍵,因為它能確保:
- MRO 鏈條的完整執行:每個類別的方法只會被執行一次,避免重複呼叫(例如鑽石繼承問題)。
- 合作式多重繼承 (Cooperative Multiple Inheritance):只要每個類別都正確呼叫
super(),控制權就會沿著 MRO 鏈條傳遞下去,直到最頂層的object類別。
讓我們看一個範例,觀察 super() 如何沿著 MRO 傳遞呼叫:
class A:
def __init__(self):
print("A init")
super().__init__()
class B(A):
def __init__(self):
print("B init")
super().__init__()
class C(A):
def __init__(self):
print("C init")
super().__init__()
class D(B, C):
def __init__(self):
print("D init")
super().__init__()
d = D()
輸出:
D init
B init
C init
A init
Mixin (混入模式)
Mixin 是一種特殊的設計模式,它是一個包含了特定功能方法的類別,但它的設計目的不是用來獨立實例化 (instantiated),而是用來混入 (mix in) 到其他子類別中,以提供額外的功能。
Mixin 的特點:
- 單一職責:通常只負責一組特定的功能(例如:將物件轉為 JSON、記錄 Log、權限驗證)。
- 不獨立使用:不應該單獨建立 Mixin 的實例。
- 非階層關係:它代表的是 "can-do"(能做什麼)的特徵,而不是傳統繼承的 "is-a"(是什麼)的關係。
使用 Mixin 可以讓我們透過組合的方式為類別添加功能,避免過度複雜的繼承階層。
class JsonMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class LogMixin:
def log(self, message):
print(f"[{self.__class__.__name__}] {message}")
class User(JsonMixin, LogMixin):
def __init__(self, name, email):
self.name = name
self.email = email
user = User("Alice", "alice@example.com")
print(user.to_json()) # {"name": "Alice", "email": "alice@example.com"}
user.log("User created") # [User] User created
抽象類別 (Abstract Base Classes)
抽象類別 (ABC) 是一種不能被實例化的類別,它的主要用途是定義介面 (Interface) 和規範子類別必須實作的方法。這有點像是定義一份「合約」,任何繼承此抽象類別的子類別,都必須遵守這份合約(實作指定的方法),否則該子類別也不能被實例化。
在 Python 中,我們使用 abc 模組來實現抽象類別:
- 類別必須繼承
abc.ABC。 - 使用
@abc.abstractmethod裝飾器來標記抽象方法。
這在大型專案開發時非常有用,能確保不同的子類別都擁有一致的 API 介面。
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
# shape = Shape() # TypeError: 不能實例化抽象類別
rect = Rectangle(5, 3)
print(rect.area()) # 15
print(rect.perimeter()) # 16
實際範例
from abc import ABC, abstractmethod
class Employee(ABC):
def __init__(self, name, salary):
self.name = name
self.salary = salary
@abstractmethod
def calculate_bonus(self):
pass
def __str__(self):
return f"{self.name} - ${self.salary}"
class Manager(Employee):
def __init__(self, name, salary, team_size):
super().__init__(name, salary)
self.team_size = team_size
def calculate_bonus(self):
return self.salary * 0.2 + self.team_size * 100
class Developer(Employee):
def __init__(self, name, salary, programming_languages):
super().__init__(name, salary)
self.programming_languages = programming_languages
def calculate_bonus(self):
return self.salary * 0.1 + len(self.programming_languages) * 50
# 使用
manager = Manager("Alice", 80000, 5)
developer = Developer("Bob", 70000, ["Python", "JavaScript", "Go"])
print(f"{manager.name}'s bonus: ${manager.calculate_bonus()}")
# Alice's bonus: $16500.0
print(f"{developer.name}'s bonus: ${developer.calculate_bonus()}")
# Bob's bonus: $7150.0