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) 可以更直觀的設定 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 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) p , p 就是指派運算子右邊的值,這裡多了條件設定,也就是設定值必須大於整數 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 陳述的用法。
由於設定屬性 a 有 raise 陳述,因此要放到 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 封裝屬性已刪除 |
> |
封裝的原始目的是要保護資料安全,也就是不讓外部程式可以直接用小數點 . 任意重新設定屬性,但是後來技術持續發展,封裝變成設定屬性的技巧,所以雖然標題是封裝,但其實是在討論怎麼設定可以保護資料安全。