這個單元我一直很想學習,物件導向 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) 傳送訊息,改變物件的狀態或要求物件執行某些行為(例如更新成績)。
物件導向的設計適合用於建立遊戲角色,都會有基本的名字、年齡、技能。如果我不用物件導向的方式設計程式,我就要在每個角色都複製貼上名字、年齡、技能,再更改數值。
Class 是用來定義物件的模板,裡面可以包含屬性(特性)和方法(功能)。
接下來,我們來看如何建構「模板」Class 吧!
__init__
(建構子方法)、self
__init__
是 Python 中的建構子方法,也叫「初始化方法」。self
是物件本身self
代表這個物件的自己,就像你在補習班的個人資料表。self.屬性名稱 = 值
,這樣物件才能記住你的資料。物件 = 類別(參數...)
來創造物件時,__init__
方法就會自動被呼叫。Q1:一定要有 self
嗎?
A:是的,self
是 Python 的規範,代表物件自己,要去接住之後創立的實體。不
Q2:如果沒寫 __init__
會怎樣?
A:物件還是可以被創造,但沒有任何屬性被初始化,等於一個「空白資料表」。
student1 = Student("小明", "高三", "數學")
被執行時: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()
抽象化 是指隱藏不必要的細節,僅保留物件的核心功能。
抽象化的核心概念:
在程式中,如果我們要設計一個「動物」的模板,但不想指定每種動物的具體叫聲(因為每個動物的叫聲不同),就可以使用抽象化(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() # 輸出: 喵喵!
我會用徒弟承襲師傅的武功,又可以更改部分內容創造自己的武功,再傳給自己的徒弟。
Animal
# 定義父類別 (父母)
class Animal:
def __init__(self, name):
self.name = name
def eat(self): #method吃東西
print(f"{self.name} 正在吃東西。")
Dog
與 Cat
# 定義子類別 (繼承父類別 Animal)
class Dog(Animal):
def bark(self):
print(f"{self.name} 說:汪汪!")
class Cat(Animal):
def meow(self):
print(f"{self.name} 說:喵喵!")
# 創建物件
dog = Dog("小黑")
cat = Cat("小白")
# 呼叫繼承的屬性與方法
dog.eat() # 繼承自 Animal 類別
dog.bark() # 子類別的方法
cat.eat() # 繼承自 Animal 類別
cat.meow() # 子類別的方法
小黑 正在吃東西。
小黑 說:汪汪!
小白 正在吃東西。
小白 說:喵喵!
有時候,子類別可能需要改寫(覆寫)父類別的方法,例如重新設計行為:
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() # 輸出: 小白 說:喵喵!
在程式設計中,封裝 就是隱藏物件的內部資料,只讓外界透過受控的方式存取和修改。
白話文的比喻是 封裝(Encapsulation) 就像老師會保管學生的個人成績,不讓其他學生直接查看,但允許合法查詢,例如家長來問時,老師會提供「經過篩選的資料」。
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}")
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 之間!
多型 表示不同的類別可以有相同的方法名稱,但表現不同的行為。例如,狗和貓都能發出聲音,但聲音不同。
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
:自動初始化物件屬性。既然提到程式風格,我再補充一些常聽到的程式設計觀念:
Imperative vs Declarative Programming
重點:描述「如何做」,一步步執行每個操作。
# 計算從 1 到 5 的總和(命令式)
total = 0
for i in range(1, 6):
total += i
print(f"總和是: {total}")
你只需要描述想要的結果,不必關心怎麼實現。
sum()
)# 計算從 1 到 5 的總和(聲明式)
total = sum(range(1, 6))
print(f"總和是: {total}")
程序式程式設計就像補習班每天上課的流程:
重點: 把程式分成具體步驟的程序或功能,重複使用。
# 程序式設計:計算圓的面積
import math
# 定義一個函數(程序)
def calculate_area(radius):
return math.pi * radius ** 2
# 使用程序
area = calculate_area(5)
print(f"圓的面積是: {area:.2f}")
函數式程式碼範例(Python - 使用 map()
)
# 使用函數式程式設計(map)
numbers = [1, 2, 3, 4, 5]
# 定義一個函數,將數字平方
def square(n):
return n ** 2
# 使用函數式程式設計應用
squared_numbers = list(map(square, numbers))
print(squared_numbers)