這個單元我一直很想學習,物件導向 Object Oriented Programming 聽起來是個很酷的功能,而且是只有 Python 可以使用,沒想到事實是:
物件導向是一種程式設計的概念,而Python 是一種物件導向語言是「不正確」的說法
學校老師沒教你 OOP 物件導向的秘密 Why is Java NOT an OOP Language? 【電腦說人話】(CC字幕)
簡單來說,物件導向程式設計就像創立一個「烤餅乾的模板」(Class),而每個用這個模板烤出來的餅乾就是「物件」(Instance)。餅乾裡的內餡可以依照需求調整,但是外型是根據 Class 為基底創造,這代表每個物件都可以有不同的屬性。更精確地來說是 OOP 的核心是 State and message 跟物件 Object 這個詞沒有直接關係。State(狀態) 指的是物件的屬性值(Attributes),用來描述物件(例如某學生)當前的數據或狀態。再透過Message(訊息) ,物件之間的溝通方式,透過方法呼叫(Method Call) 傳送訊息,改變物件的狀態或要求物件執行某些行為(例如更新成績)。
物件導向的設計適合用於建立遊戲角色,都會有基本的名字、年齡、技能。如果我不用物件導向的方式設計程式,我就要在每個角色都複製貼上名字、年齡、技能,再更改數值。
1. Class & Method (類別與方法)
Class 是用來定義物件的模板,裡面可以包含屬性(特性)和方法(功能)。
- 屬性(Attributes):物件的特性,如名字、年齡等。
- 方法(Methods):物件的行為或功能,如走路、說話等。方法是需要被「呼叫」而不是像屬性一開始就建立好。我覺得用「行動」來解釋會比方法
接下來,我們來看如何建構「模板」Class 吧!
2. __init__
(建構子方法)、self
- 建構子方法(Initializer)
__init__
是 Python 中的建構子方法,也叫「初始化方法」。- 它會在建立物件的瞬間自動執行,用來設定物件的屬性(資料)就會馬上帶入姓名、學號。例如我建立一個學生的基本資訊會有姓名、學號,當我建立學生 A,只要依序輸入「bicky, 10530」,程式就知道我輸入的是姓名和學號。
- 小提醒:字要拼對,我一開始寫 initi 所以一直跑不出來
self
是物件本身self
代表這個物件的自己,就像你在補習班的個人資料表。- 任何屬性都要寫成
self.屬性名稱 = 值
,這樣物件才能記住你的資料。
- 自動呼叫,無需手動啟動
- 當你用
物件 = 類別(參數...)
來創造物件時,__init__
方法就會自動被呼叫。 - 你只需要把必要的參數傳進去,程式會幫你完成初始化的設定。
- 當你用
Q1:一定要有 self
嗎?
A:是的,self
是 Python 的規範,代表物件自己,要去接住之後創立的實體。不
Q2:如果沒寫 __init__
會怎樣?
A:物件還是可以被創造,但沒有任何屬性被初始化,等於一個「空白資料表」。
程式碼範例:創造補習班學生名單
程式解釋
- 當
student1 = Student("小明", "高三", "數學")
被執行時: - Python 自動呼叫 __init__,幫 student1 設定:self.name = "小明"self.grade = "高三"self.course = "數學"
- 當我們呼叫
student1.show_info()
時(我把"."想成發動某功能),物件會記住這些屬性並輸出:姓名: 小明, 年級: 高三, 課程: 數學
# 定義一個學生類別
class Student:
# 初始化方法 (告訴程式,第一個第二第三個值個別是什麼」
def __init__(self, name, grade, course):
self.name = name # 記錄學生名字. 可以解釋成「的」
self.grade = grade # 記錄學生年級
self.course = course # 記錄報名的課程
# 定義一個方法:顯示學生資料
def show_info(self):
print(f"姓名: {self.name}, 年級: {self.grade}, 課程: {self.course}")
# 創建學生物件
student1 = Student("小明", "高三", "數學")
student2 = Student("老楊", "高二", "英文")
# 顯示學生資料
student1.show_info()
student2.show_info()
3. Abstraction (抽象化)
抽象化 是指隱藏不必要的細節,僅保留物件的核心功能。
抽象化的核心概念:
- 隱藏細節,展示功能。
- 使用者不需要知道內部的運作,只要知道怎麼用即可。
- 程式設計應用場景:
- 設計抽象類別,規範子類別的行為,不需要在抽象類別中實作功能。
- 子類別根據具體需求實作抽象方法。
在程式中,如果我們要設計一個「動物」的模板,但不想指定每種動物的具體叫聲(因為每個動物的叫聲不同),就可以使用抽象化(Abstraction)。
例子:建立不同動物的行為 (不用知道這個聲音怎麼發出來)
此程式碼由 ChatGPT 產生
# 引入抽象類別模組
from abc import ABC, abstractmethod
# 抽象類別: Animal
class Animal(ABC):
# 抽象方法 (沒有實作)
@abstractmethod
def make_sound(self):
pass # 這裡什麼都不做,等子類別來實作
# 繼承抽象類別
class Dog(Animal):
def make_sound(self):
print("汪汪!") # 實作叫聲方法
class Cat(Animal):
def make_sound(self):
print("喵喵!") # 實作叫聲方法
# 創建物件並呼叫方法
dog = Dog()
cat = Cat()
dog.make_sound() # 輸出: 汪汪!
cat.make_sound() # 輸出: 喵喵!
Inheritance 的三大重點:
我會用徒弟承襲師傅的武功,又可以更改部分內容創造自己的武功,再傳給自己的徒弟。
- 繼承功能,避免重複程式碼
子類別可以直接使用父類別的屬性與方法,不必重新寫一遍。 - 擴展功能(方法覆寫)
子類別可以重新定義(覆寫)父類別的方法,修改其行為。 - 多層繼承(進階概念)
子類別還能再被其他類別繼承,形成「多層繼承」結構。
程式範例:動物與寵物 (Inheritance)
1. 設計父類別:Animal
# 定義父類別 (父母)
class Animal:
def __init__(self, name):
self.name = name
def eat(self): #method吃東西
print(f"{self.name} 正在吃東西。")
2. 設計子類別:Dog
與 Cat
# 定義子類別 (繼承父類別 Animal)
class Dog(Animal):
def bark(self):
print(f"{self.name} 說:汪汪!")
class Cat(Animal):
def meow(self):
print(f"{self.name} 說:喵喵!")
3. 使用物件:創造狗和貓的實例
# 創建物件
dog = Dog("小黑")
cat = Cat("小白")
# 呼叫繼承的屬性與方法
dog.eat() # 繼承自 Animal 類別
dog.bark() # 子類別的方法
cat.eat() # 繼承自 Animal 類別
cat.meow() # 子類別的方法
輸出結果:
小黑 正在吃東西。
小黑 說:汪汪!
小白 正在吃東西。
小白 說:喵喵!
解釋發生了什麼?
- 定義父類別 Animal:
- 它擁有 name 屬性,還有一個方法 eat()。
- 繼承父類別:
- Dog 和 Cat 繼承了 Animal,因此不需要再次定義 eat() 方法。
- 擴展功能:
- Dog 類別擁有一個 bark() 方法,專屬於狗。
- Cat 類別擁有一個 meow() 方法,專屬於貓。
- 創建物件並使用:
- 創建 dog 和 cat 物件後,可以使用從 Animal 繼承來的 eat() 方法,以及各自的新功能 bark() 和 meow()。
進階概念:覆寫 (Method Overriding)
有時候,子類別可能需要改寫(覆寫)父類別的方法,例如重新設計行為:
範例:覆寫父類別方法
class Animal:
def __init__(self, name):
self.name = name
def sound(self):
print("動物發出聲音。")
# 子類別覆寫父類別的方法
class Dog(Animal):
def sound(self): #sound改成直接說汪汪
print(f"{self.name} 說:汪汪!")
class Cat(Animal):
def sound(self):
print(f"{self.name} 說:喵喵!")
# 創建物件並測試
dog = Dog("小黑")
cat = Cat("小白")
dog.sound() # 輸出: 小黑 說:汪汪!
cat.sound() # 輸出: 小白 說:喵喵!
5. Encapsulation (封裝)
在程式設計中,封裝 就是隱藏物件的內部資料,只讓外界透過受控的方式存取和修改。
白話文的比喻是 封裝(Encapsulation) 就像老師會保管學生的個人成績,不讓其他學生直接查看,但允許合法查詢,例如家長來問時,老師會提供「經過篩選的資料」。
補習班的比喻:成績資料庫
- 學生成績存在學校的「成績資料庫」中,屬於隱私資料。
- 學生或家長不能直接改成績,因為這樣不安全。
- 如果學生想知道自己的成績,必須合法查詢,如:
- 查詢接口(Getter):詢問成績,老師會讀取並回報。
- 修改接口(Setter):如果成績有錯誤,家長提出申訴,老師經過核對後可以更改。
封裝的三大重點:
- 隱藏資料(資料保護)
- 透過私有屬性 (private attributes),不讓外界直接存取物件內部資料。
- 受控存取(安全操作)
- 提供Getter(讀取資料)和Setter(修改資料)來管理資料存取。
- 避免錯誤(資料驗證)
- 可以在 Setter 中加入條件檢查,確保資料正確。
程式範例:學生成績管理 (Encapsulation)
1. 定義學生類別 Student
class Student:
def __init__(self, name, score):
self.name = name # 公開屬性
self.__score = score # 私有屬性(封裝)在屬性前面加雙底線__
# Getter 方法(讀取分數)
def get_score(self):
return self.__score #return後不能賦值操作
# Setter 方法(修改分數,並檢查分數是否合法)
def set_score(self, score):
if 0 <= score <= 100: # 確保分數範圍在 0~100
self.__score = score
else:
print("分數必須在 0 到 100 之間!")
# 顯示學生資訊
def show_info(self):
print(f"學生: {self.name}, 分數: {self.__score}")
2. 測試物件:建立與操作 Student
# 建立學生物件
student1 = Student("小明", 85)
student1.show_info() # 輸出: 學生: 小明, 分數: 85
# 嘗試直接修改私有屬性(錯誤操作)
student1.__score = 95 # 沒有效果,因為 __score 被封裝了
student1.show_info() # 輸出仍為: 學生: 小明, 分數: 85
# 使用 Setter 方法合法修改
student1.set_score(95) # 修改成功
student1.show_info() # 輸出: 學生: 小明, 分數: 95
# 嘗試輸入無效分數
student1.set_score(150) # 輸出: 分數必須在 0 到 100 之間!
解釋發生了什麼?
- 資料保護:
- self.__score 被定義為私有屬性(前面加上雙底線 __),外部無法直接存取和更改。
- 受控存取:
- get_score() 是一個Getter,用來讀取分數。
- set_score() 是一個Setter,用來修改分數,並檢查分數範圍是否正確。
- 錯誤預防:
- 如果分數不在 0 到 100 之間,set_score() 會顯示錯誤訊息,防止無效資料。
6. Polymorphism (多型)
多型 表示不同的類別可以有相同的方法名稱,但表現不同的行為。例如,狗和貓都能發出聲音,但聲音不同。
程式碼範例:
class Dog:
def speak(self):
print("汪汪!")
class Cat:
def speak(self):
print("喵喵!")
# 多型實作
def make_sound(animal):
animal.speak()
make_sound(Dog()) # 汪汪!
make_sound(Cat()) # 喵喵!
總結:
物件導向程式設計幫助我們組織程式碼,更容易維護和擴展。關鍵概念包括:
- 類別與物件:建立資料模型。
- 方法與屬性:定義物件行為與特性。
- 建構子與
self
:自動初始化物件屬性。 - 抽象化、繼承、封裝與多型:設計更彈性、更易於管理的應用程式
小比較:
- Abstraction(抽象化)是告訴你「功能怎麼用,細節不需要知道。」
- Encapsulation(封裝)是保護資料,「不該讓外部亂改,資料只能安全存取。」
既然提到程式風格,我再補充一些常聽到的程式設計觀念:
常見的程式設計觀念

Imperative vs Declarative Programming
1. Imperative Programming (命令式程式設計)
什麼是命令式程式設計?
重點:描述「如何做」,一步步執行每個操作。
簡單比喻:煮泡麵的命令式操作
- 煮水
- 放入泡麵
- 等待 3 分鐘
- 倒入調味包
- 攪拌均勻
命令式程式碼範例(Python)
# 計算從 1 到 5 的總和(命令式)
total = 0
for i in range(1, 6):
total += i
print(f"總和是: {total}")
2. Declarative Programming (聲明式程式設計)
什麼是聲明式程式設計?
你只需要描述想要的結果,不必關心怎麼實現。
簡單比喻:外送點餐的聲明式操作
- 點餐 App:「我要一個珍珠奶茶。」
- 不需要知道如何準備珍奶,外送員直接幫你送過來。
聲明式程式碼範例(Python - 使用 sum()
)
# 計算從 1 到 5 的總和(聲明式)
total = sum(range(1, 6))
print(f"總和是: {total}")
3. Procedural Programming (程序式程式設計)
什麼是程序式程式設計?
程序式程式設計就像補習班每天上課的流程:
- 進入教室
- 開始點名
- 上課教學
- 下課擦黑板
重點: 把程式分成具體步驟的程序或功能,重複使用。
程序式程式碼範例(Python)
# 程序式設計:計算圓的面積
import math
# 定義一個函數(程序)
def calculate_area(radius):
return math.pi * radius ** 2
# 使用程序
area = calculate_area(5)
print(f"圓的面積是: {area:.2f}")
4. Functional Programming (函數式程式設計)
什麼是函數式程式設計?
簡單比喻:點飲料機(功能固定)
- 飲料機只專注於輸入與輸出:
- 投幣 + 選擇飲料 → 出飲料(結果總是一樣)。
函數式程式碼範例(Python - 使用 map()
)
# 使用函數式程式設計(map)
numbers = [1, 2, 3, 4, 5]
# 定義一個函數,將數字平方
def square(n):
return n ** 2
# 使用函數式程式設計應用
squared_numbers = list(map(square, numbers))
print(squared_numbers)
重點總結:
- Imperative (命令式):一步步指示,描述怎麼做。
- Declarative (聲明式):只描述想要的結果,不關心怎麼實現。
- Procedural (程序式):按固定程序與功能模組執行。
- Functional (函數式):專注純函數運算,沒有副作用。