Learning Objectives
- 物件導向簡介:物件、類別、實例,以及封裝、繼承、多型
- 物件導向相關名詞:類別變數、實例變數、方法、方法覆寫
- 定義與使用 Python 類別:class、__init__ 與 self
- 用繼承建立子類別,並覆寫父類別的方法
物件:資料加上會做的事
物件(object)是一種自訂的資料結構,裡面有資料(變數,稱為屬性)以及程式碼(函數,稱為方法), 可以想成「內含程式碼的資料結構」。每個物件代表一個具體、獨立的東西,它的方法定義它能做什麼、如何和其他東西互動。
你早就在用物件了:值為 7 的整數是一個物件,它有「加法」的方法;值為 8 的整數是另一個物件。
7 和 8 都屬於 Python 的整數類別(int),各自是這個類別的實例(instance)。
類別像設計圖,實例是照設計圖做出來的東西。同一張設計圖可以做出很多個,每一個有自己的資料。
物件導向的三個特性:
| 特性 | 意思 |
|---|---|
| 封裝(Encapsulation) | 把資料與操作資料的方法包在一起,避免外部任意修改內部狀態,保持資料的一致性 |
| 繼承(Inheritance) | 從已存在的類別定義新的類別,沿用父類別的屬性與方法 |
| 多型(Polymorphism) | 同一個介面(同名的方法),不同類別各有自己的做法 |
定義類別:把變數與函數包在一起
class 類別名稱: 底下縮排的內容就是類別本體。Python 會先執行一次類別本體(框架顯示 class SmartPhone),建立一個類別物件。之後用「類別名稱.屬性」取用裡面的變數與函數。
__init__ 與 self:每個實例有自己的資料
__init__() 是特殊方法,稱為建構子(constructor)或初始化方法:建立實例時 Python 會自動呼叫它。方法的第一個參數是 self,代表「這個物件本身」,呼叫時 Python 會自動傳入。看右邊:myphone 與 yourphone 是兩個不同的實例,各有自己的 name 與 sn。
Lab:Course 類別
這是 Lab 的 baseclass.py:建構子存入課程名稱與時數,兩個方法一個負責印出、一個傳回名稱。注意 display_course() 自己 print,show_course() 則用 return 把值交回給呼叫者。
物件導向名詞整理
| 名詞 | 說明 |
|---|---|
| 類別(class) | 使用者自訂的物件原型,定義這類物件有哪些屬性 |
| 物件(object)/實例(instance) | 由類別產生的單一物件;例如 c1 是 Course 類別的一個實例 |
| 實例化(instantiation) | 建立類別的實例:Course('Python', 18) |
| 資料成員(data member) | 類別變數或實例變數,儲存共用資料或個別物件的資料 |
| 實例變數(instance variable) | 在方法裡用 self.名稱 建立,只屬於該實例 |
| 類別變數(class variable) | 定義在類別內、方法外,所有實例共用一份 |
| 方法(method) | 定義在類別裡的函數,第一個參數是 self;用「物件.方法()」呼叫 |
| 方法覆寫(method overriding) | 子類別重新定義父類別的同名方法 |
| 運算子重載(operator overloading) | 讓運算子對自訂類別有不同功能,例如定義 __add__ 讓 + 可以用 |
函數重載(同名函數依引數個數或型別有不同行為)在 Python 並不支援,因為 Python 不宣告變數型別;通常改用預設引數來達成類似效果。
類別變數與實例變數
emp_count 定義在類別內、方法外,是類別變數,所有員工共用一份;self.name、self.salary 是實例變數,每位員工各一份。看右邊 Employee 類別物件裡的 emp_count,每建立一位員工就加 1。
想一想:類別與實例
下面的程式會印出什麼?
class C:
count = 0
def __init__(self):
C.count += 1
a = C()
b = C()
print(C.count, a.count)
下面的程式會印出什麼?
class Dog:
species = "canine"
d1 = Dog()
d2 = Dog()
d1.species = "wolf"
print(d1.species, d2.species, Dog.species)
myphone = SmartPhone("Pixel", "p1") 之後呼叫 myphone.ringing(),在 ringing(self) 裡的 self 是什麼?
繼承與方法覆寫
寫成 class 子類別(父類別):,子類別就繼承父類別所有的屬性與方法,只需要寫「不一樣的地方」。
- 方法覆寫:子類別定義和父類別同名的方法,就會取代父類別的版本
super().__init__(...):在子類別的方法裡呼叫父類別的同名方法,常用在建構子,先讓父類別設定共同的資料- Python 允許一個類別同時繼承多個父類別(多重繼承),初學先掌握單一繼承即可
下面的動畫裡,introduce() 只寫在 Animal,但它呼叫的 self.speak() 會依物件實際的類別找到不同的版本:這就是多型。
繼承、覆寫與多型
Dog、Cat 繼承 Animal,各自覆寫 speak();Cat 還多了 indoor,用 super().__init__(name) 沿用父類別的建構子。迴圈對三個物件呼叫同一個 introduce(),注意每一次跳進哪一個 speak()。
想一想:繼承與 self
下面的程式會印出什麼?
class A:
def hi(self):
return "A"
def show(self):
print(self.hi(), end=" ")
class B(A):
def hi(self):
return "B"
B().show()
A().show()
下面的程式執行時會發生什麼事?
class P:
def ring():
print("ring")
P().ring()
實作:矩形與正方形
完成兩個 TODO:Rectangle.area() 傳回寬乘高;Square 繼承 Rectangle,建構子只收一個邊長,用 super().__init__() 把寬和高都設成邊長。完成後應該印出 矩形 12 與 正方形 25。
重點精華回顧
- 物件=屬性(資料)+方法(函數);類別是設計圖,實例是照設計圖做出來的物件。物件導向的三個特性:封裝、繼承、多型。
class 類別名稱:定義類別;類別名稱(引數)建立實例,Python 自動呼叫__init__()初始化。- 方法的第一個參數是
self,代表物件本身;物件.方法()呼叫時自動傳入。 - 實例變數用
self.名稱建立,每個實例一份;類別變數定義在類別內、方法外,所有實例共用,用類別名稱.變數存取。 - 查找屬性先找實例、再找類別;對實例指派同名屬性只會新增實例變數。
class 子類別(父類別):繼承;子類別定義同名方法就是覆寫;super().__init__()呼叫父類別的建構子。