1. 前言
Python 是一種受到廣大使用者支持的程式語言,從初學者到進階開發者皆然。其中的「覆寫(Override)」功能,是物件導向程式設計中的基本概念之一,並在許多場景中扮演重要角色。
本文將針對 Python 的覆寫機制,從基本概念到進階應用進行說明,讓初學者也能輕鬆理解並在實務中加以活用。
什麼是覆寫(Override)?
覆寫(Override)是指在子類別(衍生類別)中,重新定義父類別(基底類別)中已定義的方法。透過重新定義,可以帶來以下優點:
- 提升靈活性:善用繼承,增加程式碼的重複利用率。
- 自訂功能:不必完全依賴父類別的功能,可依需求進行調整。
- 改善維護性:讓程式碼結構更清晰,後續維護更容易。
舉例來說,在 Python 的 Web 應用框架 Django 中,常會覆寫類別式視圖(Class-Based Views)來新增自訂邏輯。這些實際案例說明了覆寫在 Python 開發實務中的重要性。
本文的目的
本指南將分階段說明以下主題:
- 覆寫的基本概念與運作原理
- Python 中的實際實作方式
- 常見注意事項與錯誤避免方法
- 實用的程式碼範例
針對初學者提供基礎內容,針對中階使用者介紹進階技巧,力求成為對所有程式開發者都實用的一篇文章。接下來的章節,將詳細說明「什麼是覆寫」。
2. 什麼是覆寫
在 Python 中,覆寫(Override)是物件導向程式設計中的核心概念之一,它是一個能提升程式彈性與重用性的關鍵功能。本節將介紹覆寫的基本定義、特性,並說明它與「多載(Overload)」之間的差異。
覆寫的基本概念
覆寫是指在子類別(衍生類別)中重新定義父類別(基底類別)中已經定義的方法。這麼做有以下幾個優點:
- 保留父類功能的同時進行自訂
非常適合在子類中加入特有的處理邏輯。 - 在保持一致性的前提下改變行為
提供統一的介面,同時實作不同的行為。
以下是覆寫的基本範例:
class Parent:
def greet(self):
print("Hello from Parent!")
class Child(Parent):
def greet(self):
print("Hello from Child!")
# 執行範例
parent = Parent()
child = Child()
parent.greet() # 輸出:Hello from Parent!
child.greet() # 輸出:Hello from Child!
在這個例子中,greet
方法在父類與子類中都有定義。當從子類的實例呼叫時,會執行子類中重新定義的處理內容。
與多載(Overload)的差異
初學者常會混淆「覆寫(Override)」與「多載(Overload)」,雖然它們名稱相似,但本質上是不同的概念。
- 覆寫(Override)
指在子類中重新定義父類的方法。
行為會在執行時(Runtime)決定。 - 多載(Overload)
指定義多個相同名稱但引數不同的方法或函式。
Python 原生並不直接支援多載,但可以透過@overload
裝飾器等方式達成類似效果。
以下是使用 Python 模擬多載的範例:
from typing import overload
class Calculator:
@overload
def add(self, x: int, y: int) -> int: ...
@overload
def add(self, x: str, y: str) -> str: ...
def add(self, x, y):
return x + y
calc = Calculator()
print(calc.add(2, 3)) # 輸出:5
print(calc.add("a", "b")) # 輸出:ab
覆寫常見的應用情境
覆寫通常會在以下幾種場景中使用:
- GUI 程式設計
自訂按鈕或元件的行為。 - Web 框架
在 Django 或 Flask 中擴充類別式視圖。 - 遊戲開發
自訂角色或物件的行為。
3. 如何在 Python 中實作覆寫
在 Python 中實作覆寫的方法相當簡單,但要正確理解父類與子類之間的關係,以及 super()
的使用方式。本節將說明從基本覆寫流程到多重繼承中需注意的事項。
基本的覆寫實作方式
在 Python 中,只需在子類中定義與父類相同名稱的方法,就能實現覆寫。
以下是一個基本的覆寫範例:
class Parent:
def greet(self):
print("Hello from Parent!")
class Child(Parent):
def greet(self):
print("Hello from Child!")
# 執行範例
parent = Parent()
child = Child()
parent.greet() # 輸出:Hello from Parent!
child.greet() # 輸出:Hello from Child!
在此例中,Child
類別中的 greet
方法覆寫了 Parent
類別的對應方法。因此當呼叫 child.greet()
時,會執行子類中的邏輯。
使用 super()
進行覆寫
有時覆寫的目的是擴充而非完全取代父類的方法。這種情況下,super()
函式就非常有用。
以下是一個使用 super()
的範例:
class Parent:
def greet(self):
print("Hello from Parent!")
class Child(Parent):
def greet(self):
super().greet()
print("And Hello from Child!")
# 執行範例
child = Child()
child.greet()
# 輸出:
# Hello from Parent!
# And Hello from Child!
在這個例子中,Child
類別呼叫 super().greet()
來執行父類的方法,然後再加入子類自訂的邏輯。
不使用 super()
的情況比較
如果不使用 super()
,則需要明確指定父類名稱來呼叫方法。
class Parent:
def greet(self):
print("Hello from Parent!")
class Child(Parent):
def greet(self):
Parent.greet(self)
print("And Hello from Child!")
# 執行範例
child = Child()
child.greet()
# 輸出:
# Hello from Parent!
# And Hello from Child!
這種方式需要手動指定父類名稱,當遇到多重繼承或複雜的繼承結構時,容易出錯。因此建議優先使用 super()
。
多重繼承下的覆寫與 MRO
Python 支援多重繼承,這時需理解哪一個父類的方法會被優先呼叫。這取決於 MRO(方法解析順序)。
class A:
def greet(self):
print("Hello from A!")
class B(A):
def greet(self):
print("Hello from B!")
class C(A):
def greet(self):
print("Hello from C!")
class D(B, C):
pass
# 執行範例
d = D()
d.greet() # 輸出:Hello from B!
在此例中,D
類別同時繼承自 B
與 C
,但因 MRO 的規則,B
的 greet
方法優先被執行。
如何確認 MRO
可以使用 __mro__
屬性或 mro()
方法來檢視類別的 MRO。
print(D.__mro__)
# 輸出:(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
這個輸出表示,方法的解析順序為 D → B → C → A → object
。
4. 實戰!Python 覆寫的程式碼範例
為了加深對覆寫的理解,以下將介紹幾個實用的程式碼範例,涵蓋基本方法的覆寫、__init__
的覆寫以及多重繼承的應用場景。
1. 覆寫方法(Method Override)
方法覆寫是 Python 中最常見的覆寫形式。透過在子類中重新定義父類的方法,可加入自訂邏輯。
class Animal:
def speak(self):
return "I make a sound"
class Dog(Animal):
def speak(self):
return "Woof!"
# 執行範例
animal = Animal()
dog = Dog()
print(animal.speak()) # 輸出:I make a sound
print(dog.speak()) # 輸出:Woof!
在這個範例中,Dog
類別的 speak
方法覆寫了 Animal
類別的方法,因此在呼叫 dog.speak()
時會執行子類中的邏輯。
2. 覆寫建構子(__init__
)
若想自訂類別的初始化邏輯,可以覆寫 __init__
方法。透過 super()
呼叫父類的初始化邏輯,同時加入子類特有的屬性。
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, my name is {self.name}"
class Student(Person):
def __init__(self, name, student_id):
super().__init__(name)
self.student_id = student_id
def greet(self):
return f"Hello, my name is {self.name} and my student ID is {self.student_id}"
# 執行範例
student = Student("Alice", "S12345")
print(student.greet())
# 輸出:Hello, my name is Alice and my student ID is S12345
在這個例子中,Student
類別覆寫了建構子,使用 super().__init__(name)
繼承父類初始化邏輯,並加入 student_id
屬性。
3. 多重繼承下的覆寫與 MRO
在多重繼承的情況下,哪個類別的方法會被執行,會依據 MRO(方法解析順序)決定。
class Vehicle:
def description(self):
return "This is a vehicle"
class Car(Vehicle):
def description(self):
return "This is a car"
class Boat(Vehicle):
def description(self):
return "This is a boat"
class AmphibiousVehicle(Car, Boat):
pass
# 執行範例
amphibious = AmphibiousVehicle()
print(amphibious.description())
# 輸出:This is a car
這個例子中,AmphibiousVehicle
同時繼承 Car
和 Boat
,但由於 MRO 的關係,會優先使用 Car
的 description
方法。
建議在這種情況下也檢查 MRO 以確認方法呼叫順序:
print(AmphibiousVehicle.__mro__)
# 輸出:(<class '__main__.AmphibiousVehicle'>, <class '__main__.Car'>, <class '__main__.Boat'>, <class '__main__.Vehicle'>, <class 'object'>)
4. 實務應用範例:Django 的 View 類別覆寫
在 Python 的 Web 框架 Django 中,常會覆寫類別式視圖(Class-Based Views)來新增自訂邏輯。
from django.views import View
from django.http import HttpResponse
class MyView(View):
def get(self, request):
return HttpResponse("This is a GET request")
class CustomView(MyView):
def get(self, request):
response = super().get(request)
return HttpResponse(response.content + b" Customized!")
# 此範例中,CustomView 覆寫了 MyView 的 GET 請求處理方法。
重點整理
- 方法覆寫是將邏輯從父類轉移到子類並進行自訂的重要機制。
- 使用
super()
可讓你在覆寫時保留並擴充父類的處理流程。 - 在多重繼承時,理解 MRO(方法解析順序)是避免混淆和錯誤的關鍵。
- 在實務開發中,覆寫常被用來實作客製化的功能。

5. 覆寫時的注意事項
覆寫雖然是非常強大且實用的功能,但若使用不當,可能會導致程式難以維護,甚至在執行時產生錯誤。本節將說明在 Python 中使用覆寫時應注意的幾個重點。
super()
的正確使用方式
透過 super()
可以安全地呼叫父類的方法,但若使用方式錯誤,可能會造成非預期行為。
建議的使用方式
- 當子類不是要完全取代父類功能,而是想「擴充」它時,請使用
super()
。
class Parent:
def greet(self):
print("Hello from Parent!")
class Child(Parent):
def greet(self):
super().greet() # 呼叫父類方法
print("And Hello from Child!")
child = Child()
child.greet()
# 輸出:
# Hello from Parent!
# And Hello from Child!
常見錯誤
- 在多重繼承下誤解 super() 的行為
在多重繼承中,super()
會依照 MRO(方法解析順序)呼叫下一個類別的方法,而不是單一父類。因此若誤以為會只執行特定父類,可能會出錯。
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!")
class D(B, C):
def greet(self):
print("Hello from D!")
super().greet()
d = D()
d.greet()
# 輸出:
# Hello from D!
# Hello from B!
# Hello from C!
# Hello from A!
- 省略
super()
,改用父類名稱
直接指定父類來呼叫方法雖然可行,但會讓程式碼變得難以維護,特別是在多重繼承情況下容易出錯。
# 不建議的寫法
class Child(Parent):
def greet(self):
Parent.greet(self)
print("And Hello from Child!")
多重繼承與 MRO(方法解析順序)
多重繼承下,Python 會依照 MRO(Method Resolution Order)決定執行哪個類別的方法。若類別關係複雜,務必理解其解析順序。
查看 MRO 的方式
可透過 __mro__
屬性或 mro()
方法來確認類別的解析順序:
class A:
def greet(self):
print("Hello from A!")
class B(A):
def greet(self):
print("Hello from B!")
class C(A):
def greet(self):
print("Hello from C!")
class D(B, C):
pass
print(D.__mro__)
# 輸出:(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
過度覆寫的副作用
如果在子類中覆寫父類的幾乎所有方法,可能會失去繼承的好處,讓程式碼變得更難維護。
改善建議
- 僅覆寫必要的方法
避免覆寫所有內容,盡可能重用父類的邏輯。 - 考慮使用組合(Composition)
若繼承結構過於複雜,或多個類別需共享行為,可考慮使用組合的方式實現。
class Engine:
def start(self):
print("Engine started")
class Car:
def __init__(self):
self.engine = Engine()
def start(self):
self.engine.start()
print("Car is ready to go!")
car = Car()
car.start()
# 輸出:
# Engine started
# Car is ready to go!
除錯時的注意事項
當覆寫的方法未如預期運作時,建議檢查以下幾點:
- 是否正確地在子類中呼叫了父類方法?
- 是否正確使用了
super()
? - 類別的 MRO 是否符合預期?
6. 覆寫與多載的比較
覆寫(Override)與多載(Overload)常被混淆,但其實是完全不同的概念。在 Python 中,覆寫是日常開發中常見的技術,而多載則在其他語言中較為常見。本節將比較兩者的差異、Python 對多載的支援情況,以及可行的替代方案。
1. 什麼是覆寫?
覆寫是指在子類中重新定義父類的方法。這樣可以保留父類的基本功能,同時在子類中實作自訂行為。
特點
- 於執行時(Runtime)決定行為
- 需要使用繼承(類別的繼承關係)
- 子類保留父類介面但可改變其行為
範例:
class Parent:
def greet(self):
return "Hello from Parent!"
class Child(Parent):
def greet(self):
return "Hello from Child!"
parent = Parent()
child = Child()
print(parent.greet()) # 輸出:Hello from Parent!
print(child.greet()) # 輸出:Hello from Child!
2. 什麼是多載?
多載(Overload)是指使用相同名稱的函式或方法,搭配不同的參數數量或型別進行多個定義。多載通常在編譯階段就會決定使用哪一個版本。
其他語言中的範例(以 Java 為例)
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}
這個例子中,add
方法有兩個版本,會依照參數型別自動選擇對應的函式。
3. Python 中的多載
Python 並不直接支援傳統的多載機制。由於 Python 採用動態型別系統,若定義兩個相同名稱的函式,後者會覆蓋前者。
Python 中的例子
class Calculator:
def add(self, a, b):
return a + b
# 重新定義(會覆蓋前一個定義)
def add(self, a, b, c):
return a + b + c
calc = Calculator()
# calc.add(1, 2) # 錯誤:找不到接受兩個參數的方法
如上所示,第二個 add
方法會覆蓋第一個,使得原本接收兩個參數的版本無法使用。
4. Python 中實現多載的方式
雖然 Python 不支援真正的多載,但可以透過下列方法實現類似功能:
(1) 使用可變參數(*args)
class Calculator:
def add(self, *args):
return sum(args)
calc = Calculator()
print(calc.add(1, 2)) # 輸出:3
print(calc.add(1, 2, 3)) # 輸出:6
透過 *args
,可以讓函式接收任意數量的參數。
(2) 使用 @overload
裝飾器
Python 的 typing
模組提供 @overload
裝飾器,雖然它不會影響實際執行邏輯,但可提供靜態型別檢查支援。
from typing import overload
class Calculator:
@overload
def add(self, x: int, y: int) -> int: ...
@overload
def add(self, x: str, y: str) -> str: ...
def add(self, x, y):
return x + y
calc = Calculator()
print(calc.add(1, 2)) # 輸出:3
print(calc.add("a", "b")) # 輸出:ab
@overload
僅用於提示工具與型別檢查器,在執行階段不會產生實際影響。
5. 覆寫與多載的使用情境比較
- 覆寫(Override):用於子類修改父類的行為,適合用在類別繼承中。
- 多載(Overload):用於根據參數不同而提供不同實作,Python 中可透過
@overload
或*args
實現。
7. 總結
本篇文章從 Python 的覆寫(Override)基本概念開始,詳細介紹了其實作方法、注意事項,甚至與多載(Overload)的比較。以下是重點整理,幫助你更有效地掌握並應用覆寫技巧。
覆寫的基本作用
- 覆寫是指子類重新定義父類的方法,讓我們可以進行更彈性的自訂。
- 透過覆寫,可維持一致的介面,同時實現不同的行為。
在 Python 中實作覆寫的方法
- 只要在子類中定義與父類同名的方法,即可完成覆寫。
- 使用
super()
可在覆寫中呼叫父類的方法,進行擴充而非取代。 - 多重繼承時,應確認 MRO(方法解析順序),確保方法呼叫順序如預期。
使用覆寫時的注意事項
- 正確使用
super()
:呼叫父類方法時應善用super()
,避免硬編碼父類名稱。 - 避免過度覆寫:僅覆寫必要的方法,維持程式碼可維護性。
- 小心設計多重繼承:過度複雜的繼承結構可能導致難以預測的行為。
與多載的差異與應用方式
- 覆寫與多載雖名稱類似,但概念不同。
- 覆寫:子類重新定義父類的方法。
- 多載:定義多個具有相同名稱但參數不同的方法(Python 不原生支援)。
- 在 Python 中,可透過
@overload
或*args
模擬多載行為。
覆寫在實務中的應用
- Web 框架:在 Django 或 Flask 中覆寫類別式視圖以自訂行為。
- GUI 程式設計:自訂按鈕或元件的事件處理。
- 遊戲開發:透過繼承與覆寫調整角色或物件的行為。
結語
覆寫是 Python 中物件導向設計的重要技能之一。只要掌握其原理與正確的使用方式,就能撰寫出更靈活、可維護的程式碼。希望透過本篇教學,能幫助你在專案與實務開發中更好地運用覆寫。
8. 常見問答(FAQ)
以下整理了一些關於 Python 覆寫(Override)常見的疑問,適合初學者與中階開發者快速查閱與理解。
Q1: 覆寫(Override)與多載(Overload)有什麼主要差異?
A:
- 覆寫 是指子類別重新定義父類別的方法。這是基於類別繼承的行為。
- 範例:子類將父類的
greet()
方法改寫為不同的行為。 - 多載 是指定義多個同名但引數不同的方法。在 Python 中雖不直接支援,但可透過
@overload
或可變參數模擬類似效果。
Q2: 一定要使用 super()
嗎?
A:
- 基本上建議使用。使用
super()
可以安全且正確地呼叫父類的方法,尤其在多重繼承時可避免錯誤。 - 特殊情況 下可直接指定父類名稱來呼叫,但僅建議在繼承結構非常簡單時使用。
Q3: 如何讓子類完全取消父類的方法?
A:
若想讓子類不執行父類的方法,可在子類中覆寫該方法並留空處理:
class Parent:
def greet(self):
print("Hello from Parent!")
class Child(Parent):
def greet(self):
pass # 子類覆寫但不做任何事
child = Child()
child.greet() # 沒有輸出
Q4: Python 可以實現多載嗎?
A:
雖然 Python 不支援傳統的多載機制,但可使用以下方式實現類似行為:
- 使用可變參數:
class Calculator:
def add(self, *args):
return sum(args)
calc = Calculator()
print(calc.add(1, 2)) # 輸出:3
print(calc.add(1, 2, 3)) # 輸出:6
- 使用
@overload
裝飾器(僅用於型別提示):
from typing import overload
class Calculator:
@overload
def add(self, x: int, y: int) -> int: ...
@overload
def add(self, x: str, y: str) -> str: ...
def add(self, x, y):
return x + y
calc = Calculator()
print(calc.add(1, 2)) # 輸出:3
print(calc.add("a", "b")) # 輸出:ab
Q5: 在多重繼承中,覆寫行為不如預期怎麼辦?
A:
多重繼承會依照 MRO(方法解析順序)決定哪個類別的方法被呼叫。若結果與預期不符,請檢查以下項目:
- 檢查 MRO:使用
類別.__mro__
或類別.mro()
來查看順序。
print(ClassName.__mro__)
- 正確使用
super()
:確保父類方法依 MRO 被正確呼叫。 - 簡化繼承順序:如果結構過於複雜,考慮重新設計。
Q6: 如果不使用覆寫與繼承,有其他自訂行為的方式嗎?
A:
可以使用組合(Composition),即在類別中包含另一個物件的方式。這種方式比繼承更靈活,適合重用功能又不想受到繼承限制的情境。
class Engine:
def start(self):
print("Engine started")
class Car:
def __init__(self):
self.engine = Engine()
def start(self):
self.engine.start()
print("Car is ready to go!")
car = Car()
car.start()
# 輸出:
# Engine started
# Car is ready to go!
當想避免過度使用繼承時,組合是一個不錯的選擇。