昨天我們學會了:
__init__:建構子,用來初始化資料self:物件自己的指標,幫我們區分不同物件那麼今天,我們要讓物件變得「更聰明、更有互動性」——
不只會自顧自跑程式,而是能互相連結、交換資料,
還要學會超實用的觀念:繼承(Inheritance)!
想像你在經營一間補習班:
這時候就出現「物件互動」:
不同物件之間彼此連結、共享資料。
最關鍵的觀念是:「資料同步!」
如果用傳統函式寫法,要追蹤「誰選了哪門課」超容易出錯。
但透過物件導向思維,我們可以讓:
只要透過互相呼叫方法,就能讓資料自動同步!
範例:學生選課系統
class Course:
    def __init__(self, name):
        self.name = name
        self.students = []  # 課程裡的學生清單
    
    def add_student(self, student):
        self.students.append(student)
        print(f"{student.name} 選了課程 {self.name}")
    def show_students(self):
        print(f"課程 {self.name} 的學生有:")
        for s in self.students:
            print(f"- {s.name}")
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade
        self.courses = []  # 學生選的課程清單
    
    def enroll(self, course):
        self.courses.append(course)
        course.add_student(self)
        print(f"{self.name} 已成功加入 {course.name}")
    
    def show_courses(self):
        print(f"{self.name} 選的課程有:")
        for c in self.courses:
            print(f"- {c.name}")
# 建立學生
alice = Student("Alice", 5)
bob = Student("Bob", 6)
# 建立課程
math = Course("數學")
english = Course("英文")
# 學生選課
alice.enroll(math)
alice.enroll(english)
bob.enroll(math)
輸出:
說明:
alice.enroll(math)
math.add_student(alice) → 數學課也知道 Alice 選了它只更新一邊資料
→ 導致學生記得有選課,課程卻沒記錄他。
忘記呼叫方法
→ 把 add_student 寫好卻沒執行,畫面沒任何變化。
查詢資料:
alice.show_courses()
bob.show_courses()
math.show_students()
english.show_students()
輸出會完整顯示:
輸出:

想像你有三個角色:
他們都有「名字」和「年齡」,但各自又有不同功能(教課、讀書、打卡)。
若你三個類別都寫一遍 name、age、say_hello(),會重複超多程式碼!
如果還是有點不太清楚的話,這邊換個方式解說:
Person 是「人」的基本規格(name、age),學生(Student)與老師(Teacher)都是「人」,但會有額外屬性(學生有年級、老師有科目)。Person,學生與老師直接「繼承」,就不用在每個類別裡重複寫 name 的處理。好處:
範例:Person → Student & Teacher
class Person:
    def __init__(self, name):
        self.name = name
    
    def show_name(self):
        print(f"姓名:{self.name}")
class Student(Person):
    def __init__(self, name, grade):
        super().__init__(name)  # 呼叫父類別初始化 name
        self.grade = grade
    
    def show_info(self):
        print(f"學生: {self.name}, 年級: {self.grade}")
class Teacher(Person):
    def __init__(self, name, subject):
        super().__init__(name)
        self.subject = subject
    
    def show_info(self):
        print(f"老師: {self.name}, 教學科目: {self.subject}")
# 建立物件
s1 = Student("Alice", 5)
t1 = Teacher("Mr. Lee", "數學")
s1.show_name()   # 父類別方法
s1.show_info()   # 子類別方法
t1.show_name()
t1.show_info()
說明:
Person:父類別(共用屬性 name 與方法 show_name)。Student(Person):表示 Student 繼承 Person。
super().__init__(name):呼叫父類別的初始化,把 name 初始化好(否則 self.name 會不存在)。Student.total_students:類別變數(放在 class 層級),用來統計總學生數(注意:不是每個學生都有獨立 copy)。Teacher(Person):同理,繼承 Person,並新增 subject。輸出:
忘記 super().__init__(name, age)
→ 子類別沒呼叫父類別建構子,就會缺屬性!
把 super() 寫成 self.super() (常見錯誤)
我們再多看一種範例:
class Vehicle:  # 父類別(通用的交通工具)
    def __init__(self, brand):
        self.brand = brand
    def move(self):
        print(f"{self.brand} 正在移動中")
class Car(Vehicle):  # 子類別(汽車)
    def honk(self):
        print(f"{self.brand} 按喇叭:嗶嗶!")
class Bike(Vehicle):  # 子類別(腳踏車)
    def ring_bell(self):
        print(f"{self.brand} 按鈴:叮叮~")
Vehicle 是「父類別」,裡面定義了交通工具的基本功能。Car(Vehicle) 表示 Car 繼承 Vehicle 的所有特性。
__init__ 和 move()。honk()、Bike 有 ring_bell()。car = Car("Toyota")
bike = Bike("Giant")
car.move()       # 從父類別繼承而來
car.honk()       # 子類別自己定義
bike.move()      # 從父類別繼承
bike.ring_bell() #子類別自己定義
輸出:
說來說去,看到這邊的你應該會有一個疑惑就是:
為什麼要繼承?
想像你要開一間補習班,會有:
他們其實都有一些共通特性,像是:
但他們又有一些「特別的功能」:
如果你為每個角色都從頭寫一個 class,
那這些「共通的部分」就得重複三次,非常浪費。
所以我們可以建立一個「父類別」── Person,
然後讓 Teacher、Student、Staff 全部繼承自 Person!
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def say_hello(self):
        print(f"嗨,我是 {self.name},今年 {self.age} 歲!")
# 子類別繼承 Person
class Teacher(Person):
    def teach(self):
        print(f"{self.name} 正在上課中~")
class Student(Person):
    def study(self):
        print(f"{self.name} 正在努力讀書中 ")
# 測試
teacher = Teacher("王老師", 35)
student = Student("小明", 15)
teacher.say_hello()
teacher.teach()
student.say_hello()
student.study()
Teacher(Person) 表示 Teacher 是繼承自 Person。__init__ 和 say_hello()。輸出:
有時候子類別會想要「稍微改一下」父類別的功能。
這時候可以用「覆寫(override)」來取代父類別的方法。(延續上題)
class Student(Person):
    def say_hello(self):
        print(f"哈囉~我是學生 {self.name},我今年 {self.age} 歲喔!")
student = Student("小美", 14)
student.say_hello()  # 使用子類別版本,而非父類別的
「覆寫」就像是在繼承的基礎上「客製化」行為。
你還是「繼承」了那個功能的名字,只是裡面的內容換成你自己的版本。
輸出:
有時候你想「在保留父類別原有功能的同時,再加一些東西」。這時候就要用到 super()!
class Teacher(Person):
    def say_hello(self):
        super().say_hello()  # 先呼叫父類別的方法
        print("我是一位補習班老師")
teacher = Teacher("陳老師", 40)
teacher.say_hello()
輸出:
# ====== 類別定義區 ======
class MenuItem:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    
    def info(self):
        print(f"{self.name} - ${self.price}")
class Drink(MenuItem):
    def __init__(self, name, price, size):
        super().__init__(name, price)
        self.size = size
    
    def info(self):
        print(f" {self.name} ({self.size}) - ${self.price}")
class MainDish(MenuItem):
    def __init__(self, name, price, spicy=False):
        super().__init__(name, price)
        self.spicy = spicy
    
    def info(self):
        spicy_mark = "🌶️" if self.spicy else ""
        print(f" {self.name}{spicy_mark} - ${self.price}")
class Dessert(MenuItem):
    def __init__(self, name, price, sweetness):
        super().__init__(name, price)
        self.sweetness = sweetness
    
    def info(self):
        print(f"{self.name}(甜度:{self.sweetness}/5) - ${self.price}")
# ====== 建立菜單 ======
menu = [
    Drink("紅茶", 30, "M"),
    Drink("拿鐵", 60, "L"),
    MainDish("咖哩飯", 120, True),
    MainDish("燉飯", 150, False),
    Dessert("布朗尼", 80, 4),
    Dessert("抹茶蛋糕", 90, 3)
]
# ====== 主程式互動區 ======
print("=== 🍽️ 歡迎光臨IT人餐廳!===")
order_list = []
while True:
    print("\n--- 今日菜單 ---")
    for i, item in enumerate(menu, start=1):
        print(f"{i}. ", end="")
        item.info()
    choice = input("\n請輸入想點的餐點編號(輸入 q 結束點餐):")
    if choice.lower() == "q":
        break
    if not choice.isdigit() or not (1 <= int(choice) <= len(menu)):
        print("⚠️ 無效輸入,請重新輸入餐點編號。")
        continue
    item = menu[int(choice) - 1]
    order_list.append(item)
    print(f"✅ 已將「{item.name}」加入餐點。")
# 結帳
print("\n===🧾結帳明細 ===")
total = 0
for o in order_list:
    o.info()
    total += o.price
print(f"\n💵 總金額:${total}")
print("感謝您的光臨")
我們先定義一個父類別 MenuItem,代表菜單上的每一項。
每個項目都有 名稱 和 價格。
接著,我們創建三個子類別:
Drink(飲料)→ 有「大小杯」資訊MainDish(主餐)→ 可以標註是否「辣」Dessert(甜點)→ 有「甜度」資訊輸出:
完整輸出:
再次提醒新手常見錯誤:
self.super().__init__(name, price) 
 # 這樣寫是錯的!
因為 super() 是一個內建函式,不需要加 self.。
正確寫法:
super().__init__(name, price)
#這樣才是正確的
老實說,當初我剛學「繼承」時超級懷疑人生。
心想:「我就直接寫三個類別不行嗎?幹嘛搞這麼複雜?」
但當你開始實作像這種「餐廳系統」時,就會突然懂了:
原來繼承的重點不是省幾行程式碼,
而是讓整個系統有「延展性」與「一致性」。
今天多加一個類別,比如「湯品」,
我根本不用改前面的架構,只要繼承 MenuItem,加個新屬性就搞定。
這樣也很省時對吧!!
今天辛苦啦!希望讀者有跟上進度~這邊確實會有點抽象!!
不過多看幾次、多試幾次,就會進步很多的!!
一起加油~迎接最後一週!!時間過超快的!
那麼我們就明天見囉!!