Python 入門指南 5.0
單元 12 - 類別
類別 (class) 用來設計自己需要的物件 (object) ,也就是說類別是物件的藍圖。 Python 中設計類別使用關鍵字 (keyword) class ,裡頭可定義類別的類別屬性 (class attribute) 、類別方法 (class method) 、實體屬性 (instance attribute) 與實體方法 (instance method) 等等
class_object_name
def method_name(self):
self.instance
本書中如果單稱方法 (method) ,通常是指實體方法。
先舉一例示範如何定義類別
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # 定義類別 Demo
class Demo:
# 建立屬性的方法
def set_att(self, a, b): # 需要兩個參數
# 定義實體屬性 a, b
self.a = a
self.b = b
# 執行特定任務的方法
def do_something(self): # 沒有定義參數
# 回傳屬性 a 與 b 的相加值
return self.a + self.b
# 以下是執行部分
# 建立 Demo 型態的物件變數 d
d = Demo()
# 設定 d 的屬性
d.set_att(11, 22)
# 印出 d 屬性 a 與 b 的相加值
print(d.do_something())
# 檔名: class_demo01.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
第 2 行,關鍵字 class 後面空一格,然後接類別的識別字 (identifier) ,此例為 Demo ,最後接一個冒號
2 | class Demo:
|
下面用縮排的方式定義兩個實體方法,實體方法定義使用函數 (function) 定義相同的關鍵字 def ,注意定義實體方法同樣是關鍵字 def 之後空一格,然後接上方法的識別字,最後接上冒號
4 6 7 8 10 12 | def set_att(self, a, b): # 需要兩個參數
self.a = a
self.b = b
def do_something(self): # 沒有定義參數
return self.a + self.b
|
實體方法其實跟函數很多地方是一樣的,簡單說,實體方法可以看作是物件專屬的函數,這裡兩個實體方法 set_att() 與 do_something() 都有一個參數 (parameter) 名為 self
4 | def set_att(self, a, b): # 需要兩個參數
|
我們用草綠色標記 self ,主要因為這是個識別字,代表物件本身自己,當然可以自己取第一個參數的名稱,不過習慣上都用 self 。
標準程式庫 (standard library) 中的識別字同樣用草綠色標記。
set_att() 有兩個參數 a 與 b ,這兩個參數用來設定實體屬性 self.a 與 self.b
6 7 | self.a = a
self.b = b
|
物件又稱為實體 (instance) ,因為這是從類別所創造出來的。由於 self 在實體方法定義中表示物件本身,因此凡是實體方法中用 self. 接識別字都是實體屬性,這是物件的屬性值。
do_something() 則是回傳兩個實體屬性的相加值
10 12 | def do_something(self): # 沒有定義參數
return self.a + self.b
|
do_something() 並沒有定義參數,而底下是直接用 self 來存取實體屬性,這裡可以看出方法與函數的主要差別,函數能夠運算的除了參數外,就是函數內定義的區域變數 (local variable) ,實體方法除了參數跟區域變數外,還加上能夠直接存取實體屬性。
下面建立 Demo 物件,然後呼叫 set_att() 與 do_something()
16 18 20 | d = Demo()
d.set_att(11, 22)
print(d.do_something())
|
建立實體物件類似呼叫函數,指派運算子後面為類別名稱加上小括號,此例執行結果如下
$ python class_demo01.py |
33 |
$ |
以上介紹的實體屬性跟實體方法,類別也可以有類別專屬的類別屬性跟類別方法,舉例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # 定義類別 Demo2
class Demo2:
# 定義類別屬性 a, b
a = 11
b = 22
# 執行特定任務的方法
@classmethod
def do_something(cls):
cls.a += 1
cls.b += 1
return cls.a + cls.b
# 以下是執行部分
# 印出 Demo2 類別屬性 a 與 b 的相加值
print(Demo2.do_something())
# 檔名: class_demo02.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
類別屬性的定義是放在定義類別的關鍵字的下一行
2 4 5 | class Demo2:
a = 11
b = 22
|
至於類別方法是在方法定義上加上裝飾子 (decorator) @classmethod
8 9 10 11 12 | @classmethod
def do_something(cls):
cls.a += 1
cls.b += 1
return cls.a + cls.b
|
裝飾子的語法高亮度採用粉紅色。
類別方法中預設採用識別字 cls 存取類別屬性,此例是在類別方法中將類別屬性遞增,然後回傳類別屬性的相加值。
底下執行部分直接用類別名稱呼叫類別方法
10 | print(Demo2.do_something())
|
執行結果如下
$ python class_demo02.py |
35 |
$ |
類別屬性跟類別方法也可以被實體物件呼叫,舉例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # 定義類別 Demo3
class Demo3:
# 定義類別屬性 count
count = 0
# count 加一
@classmethod
def add_instance(cls):
cls.count += 1
# 以下是執行部分
# 利用迴圈建立 Demo3 型態的物件變數 d
for i in range(101):
d = Demo3()
d.add_instance()
# 印出 d 類別屬性 count
print(d.count)
# 檔名: class_demo03.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
第 4 行定義的類別屬性用來計算實體物件建立的數量
4 | count = 0
|
然後在類別方法 add_instance() 中將 count 遞增
7 8 9 | @classmethod
def add_instance(cls):
cls.count += 1
|
下面執行部分是用 for 迴圈建立 Demo3 物件總計 101 次
13 14 15 | for i in range(101):
d = Demo3()
d.add_instance()
|
最後印出 count 值
17 | print(d.count)
|
執行結果如下
$ python class_demo03.py |
101 |
$ |
利用內建函數 isinstance() 可以判斷某變數是否為某類別的實體物件,舉例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 定義類別 Demo4
class Demo4:
# 暫不定義內容
pass
# 定義類別 Demo5
class Demo5:
# 暫不定義內容
pass
# 以下是執行部分
# 建立 Demo4 型態的變數 d1
d1 = Demo4()
# 判斷 d1 是否為 Demo4 的實體
print(isinstance(d1, Demo4))
# 建立 Demo4 型態的變數 d2
d2 = Demo5()
# 判斷 d2 是否為 Demo4 的實體
print(isinstance(d2, Demo4))
# 檔名: class_demo04.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
因為這裡僅需定義類別,因此 Demo4 及 Demo5 的定義都用關鍵字 pass 帶過
2 4 | class Demo4:
pass
|
關鍵字 pass 表示什麼都不做,使用 pass 是要讓所要定義的識別字,無論是函數、類別或方法的識別字在作用域中成立,如果不用 pass 或沒有定義內容會導致縮排錯誤。
執行部分先建立 Demo4 的變數 d1 ,接著印出 d1 是否為 Demo4 的實體物件
13 15 | d1 = Demo4()
print(isinstance(d1, Demo4))
|
繼續建立 Demo5 的變數 d2 ,然後印出 d2 是否為 Demo4 的實體物件
執行結果如下
$ python class_demo04.py |
True |
False |
$ |
d1 是 Demo4 的實體物件沒錯,至於 d2 則是 demo5 的實體物件。
接下來我們會介紹 Python 的物件導向程式設計 (object oriented programming) ,先看到 Python 中的特別的資料類別 (data class) ,以及建構子 (constructor) 與封裝 (encapsulation) 。
中英文術語對照 | |
---|---|
類別 | class |
類別屬性 | class attribute |
類別方法 | class method |
建構子 | constructor |
資料類別 | data class |
裝飾子 | decorator |
封裝 | encapsulation |
函數 | function |
識別字 | identifier |
實體 | instance |
實體屬性 | instance attribute |
實體方法 | instance method |
關鍵字 | keyword |
區域變數 | local variable |
方法 | method |
物件導向程式設計 | object oriented programming |
物件 | object |
參數 | parameter |
標準程式庫 | standard library |
重點整理 |
---|
1. 類別是物件設計的藍圖,每一個類別都可定義屬性及方法。 |
2. 方法如同函數,專屬於類別。實體屬性屬於由類別建立的物件,類別屬性則屬於類別,須由類別名稱調用。 |
3. self 為預設的的參數名稱,表示物件實體。 |
4. __init__() 是類別定義中特殊的方法,用來建立物件。 |
5. 定義類別屬性是直接寫在關鍵字 class 的下一行,定義類別方法要加上裝飾子 @classmethod ,裡頭可以用 cls 存取類別屬性。 |
6. 實體物件可以直接使用類別屬性及呼叫類別方法,實體方法中如果要使用類別屬性及呼叫類別方法,就要連帶使用類別名稱。 |
7. 利用 isinstance() 可以判斷參數是否為指定的類別。 |
問題與討論 |
---|
1. 方法跟函數有什麼異同? |
2. 為什麼要有類別屬性?類別屬性跟實體屬性有什麼不同? |
3. 如果不寫 self ,那可以用其他的字代替嗎? |
4. 要怎麼判斷特定變數是屬於哪一個類別? |
練習 |
---|
1. 寫一個程式 exercise1201.py ,裡頭設計一個類別 Calculator ,利用 set_value() 方法接收兩個參數 v1 及 v2 ,並利用這兩個參數設定實體屬性 value1 與 value2 ,另外定義 add() 方法,回傳 value1 與 value2 的和,執行部分採用自訂的數字建立 Calculator 物件,然後印出 add() 的回傳值。 參考程式碼 |
2. 承上題,將新程式寫在 exercise1202.py 中,裡頭設計一個類別 Calculator ,執行部分改成接收使用者輸入並使用 int() 轉換使用者輸入的字串為整數。 參考程式碼 |
3. 承上題,將新程式寫在 exercise1203.py 中,由於 int() 在遇到非整數字串的時候會發起 ValueError ,因此把型態轉換移到 set_value() 中執行,並且利用例外處理在 ValueError 的情況把兩個屬性都設定為 None 。 參考程式碼 |
4. 承上題,將新程式寫在 exercise1204.py 中,加入 substract() ,計算兩個屬性的差,並且在執行部分印出差值。 參考程式碼 |
5. 承上題,將新程式寫在 exercise1205.py 中,加入 multiply() ,計算兩個屬性的乘積,並且在執行部分印出來。 參考程式碼 |
6. 承上題,將新程式寫在 exercise1206.py 中,加入 divide() ,計算兩個屬性相除的結果,並且在執行部分印出來。 參考程式碼 |
7. 承上題,將新程式寫在 exercise1207.py 中,加入 modulo() ,計算兩個屬性取餘數的結果,並且在執行部分印出來。 參考程式碼 |
8. 寫一個程式 exercise1208.py ,裡頭設計一個類別 NumberSequence ,利用 set_value() 方法接收參數 v1 ,並利用 v1 設定實體屬性 value1 ,另外定義 factorial() 方法,回傳到 value1 的階乘值,執行部分採用使用者輸入並且印出階乘值。 參考程式碼 |
9. 承上題,將新程式寫在 exercise1209.py 中,加入類別方法 get_factorial() ,改用產生器的方式建立階乘數列。 參考程式碼 |
10. 承上題,將新程式寫在 exercise1210.py 中,加入 fibonacci() ,回傳費氏數列中的數字。 參考程式碼 |
11. 承上題,將新程式寫在 exercise1211.py 中,加入類別方法 get_fibonacci() ,改用產生器的方式建立費氏數列。 參考程式碼 |