Python 入門指南 5.0

單元 12 - 類別

~~學習進度表~~

類別 (class) 用來設計自己需要的物件 (object) ,也就是說類別是物件的藍圖。 Python 中設計類別使用關鍵字 (keyword) class ,裡頭可定義類別的類別屬性 (class attribute) 、類別方法 (class method) 、實體屬性 (instance attribute) 與實體方法 (instance method) 等等

class class_name:
    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() 有兩個參數 ab ,這兩個參數用來設定實體屬性 self.aself.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 月

因為這裡僅需定義類別,因此 Demo4Demo5 的定義都用關鍵字 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
$

d1Demo4 的實體物件沒錯,至於 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() 方法接收兩個參數 v1v2 ,並利用這兩個參數設定實體屬性 value1value2 ,另外定義 add() 方法,回傳 value1value2 的和,執行部分採用自訂的數字建立 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() ,改用產生器的方式建立費氏數列。 參考程式碼

上一頁 單元 11 - 進一步講指派與相等運算
回 Python 入門指南 5.0 首頁
下一頁 單元 13 - 資料類別、 __init__() 方法與封裝
回 Python 教材首頁
回程式語言教材首頁