Python 簡易手冊

單元 61 - 封裝

Python 是物件導向程式語言 (object-oriented programming language) ,物件導向程式設計 (object-oriented programming) 有三大特性,分別是封裝 (encapsulation) 、繼承 (inheritance) 與多型 (polymorphism) 。所謂封裝是指讓屬性 (attribute) 或方法 (method) 不能讓外部程式直接存取, Python 的封裝是在屬性或是方法前加上連續兩條底線,舉例如下

 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
# 定義類別
class Demo:
    # 定義建構子
    def __init__(self):
        # 封裝屬性
        self.__a = 0

    # 封裝方法
    def __print_a(self):
        print(self.__a)

    # 公開方法呼叫封裝方法
    def do_something(self):
        self.__print_a()

# 建立物件
d = Demo()
# 執行公開方法
d.do_something()

# 檔名: class_demo19.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 3 月

單元 54 - 類別介紹類別 (class) 的基本概念,單元 62 - 繼承會介紹物件導向程式設計的第二項特性 - 繼承,單元 64 - 多型會介紹物件導向程式設計的第三項特性 - 多型,單元 55 - 實體屬性與方法介紹如何定義實體屬性及實體方法,單元 56 - 建構子與解構子介紹如何定義建構子。

第 6 行是已封裝的實體屬性 (instance attribute) __a

 3
 4
 5
 6
    # 定義建構子
    def __init__(self):
        # 封裝屬性
        self.__a = 0

第 9 行是已封裝的實體方法 (instance method) __print_a() ,其內工作是印出封裝屬性 __a

 8
 9
10
    # 封裝方法
    def __print_a(self):
        print(self.__a)

第 13 行是公開的方法 do_something()do_something() 呼叫私有方法 __print_a()

12
13
14
    # 公開方法呼叫封裝方法
    def do_something(self):
        self.__print_a()

底下執行部分是呼叫公開方法,結果如下

> python class_demo19.py
0
>

上例如果在執行部分呼叫私有方法或是使用私有屬性都會出錯,傳統封裝之後是用公開的 getter 取得私有屬性,另外也是公開的 setter 設定屬性,例如

 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
27
28
29
# 定義類別
class Demo:
    # 定義建構子
    def __init__(self):
        # 封裝屬性
        self.__a = 0

    # getter 方法
    def get_a(self):
        return self.__a

    # setter 方法
    def set_a(self, p):
        self.__a = p

# 建立物件
d = Demo()
# 印出 getter
print(d.get_a())
# 呼叫 setter
d.set_a(10)
# 印出 getter
print(d.get_a())

# 檔名: class_demo20.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 3 月

以上用 get_a() 取得已封裝屬性 __a ,然後用 set_a() 設定已封裝屬性 __a ,執行結果如下

> python class_demo20.py
0
10
>

利用裝飾子 (decorator) 可以更直觀的設定 gettersetter ,例如

 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 定義類別
class Demo:
    # 定義建構子
    def __init__(self):
        # 封裝屬性
        self.__a = 0

    # getter
    @property
    def a(self):
        return self.__a

    # setter
    @a.setter
    def a(self, p):
        # 依條件設定
        if p > 10:
            self.__a = p
        else:
            raise Exception("屬性設定錯誤")

    # 刪除屬性
    @a.deleter
    def a(self):
        del self.__a
        print("封裝屬性已刪除")

# 建立物件
d = Demo()
# 設定屬性
try:
    d.a = 9
except Exception as e:
    print(e)
    d.a = 11
# 印出屬性
print(d.a)
# 刪除屬性
del d.a

# 檔名: class_demo21.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 3 月

第 9 行用裝飾子 @property ,表示這是個屬性,方法名稱 a 就是屬性名稱

 8
 9
10
11
    # getter
    @property
    def a(self):
        return self.__a

第 14 行用裝飾子 @a.setter ,這是屬性 a 用指派運算子 (assignment) 的設定方式,方法名稱同樣是 a ,但是加上參數 (parameter) pp 就是指派運算子右邊的值,這裡多了條件設定,也就是設定值必須大於整數 10

13
14
15
16
17
18
19
20
    # setter
    @a.setter
    def a(self, p):
        # 依條件設定
        if p > 10:
            self.__a = p
        else:
            raise Exception("屬性設定錯誤")

單元 20 - if 陳述詳細介紹 if 陳述 (statement) 的用法,單元 27 - raise 陳述介紹 raise 陳述的用法。

第 23 行用到裝飾子 @a.deleter ,這是刪除屬性執行時的方法

22
23
24
25
26
    # 刪除屬性
    @a.deleter
    def a(self):
        del self.__a
        print("封裝屬性已刪除")

單元 32 - del 陳述介紹 del 陳述的用法。

由於設定屬性 araise 陳述,因此要放到 try 底下做例外處理 (exception handling)

28
29
30
31
32
33
34
35
36
37
38
39
# 建立物件
d = Demo()
# 設定屬性
try:
    d.a = 9
except Exception as e:
    print(e)
    d.a = 11
# 印出屬性
print(d.a)
# 刪除屬性
del d.a

單元 26 - try 陳述介紹 try 陳述的用法。

執行結果如下

> python class_demo21.py
屬性設定錯誤
11
封裝屬性已刪除
>

封裝的原始目的是要保護資料安全,也就是不讓外部程式可以直接用小數點 . 任意重新設定屬性,但是後來技術持續發展,封裝變成設定屬性的技巧,所以雖然標題是封裝,但其實是在討論怎麼設定可以保護資料安全。

參考資料

上一頁 單元 60 - 資料類別與資料模型
回 Python 簡易手冊 首頁
下一頁 單元 62 - 繼承
回 Python 教材首頁
回程式語言教材首頁