封裝 (encapsulation) 是物件導向程式設計 (object-oriented programming) 的重要概念之一,主要目的是封裝類別 (class) 中的屬性 (attribute) ,讓屬性只能在類別中建立及修改。
先來看看屬性沒有封裝的情況, Demo 類別有類別屬性 (class attribute) x 及實體屬性 (instance attribute) i , x 主要用途是記錄 Demo 建立實體物件的次數,這裡可以看到屬性沒有封裝的話,類別外的程式 (program) 就可以任意修改屬性值,因此如果屬性沒有封裝,就很有可能被外部程式修改,導致程式運作異常
class Demo:
x = 0
def __init__(self, i):
self.i = i
Demo.x += 1
d = Demo(1)
print(Demo.x)
Demo.x = 9
print(Demo.x)
d.i = -1
print(d.i)
#《程式語言教學誌》的範例程式
# http://kaiching.org/
# 檔名:encapsulate01.py
# 功能:示範定義類別
# 作者:張凱慶
於命令列執行以上程式,結果如下
$ python3 encapsulate01.py |
1 |
9 |
-1 |
$ |
這裡 Demo2 類別示範了封裝的寫法,就是在屬性識別字前加上連續兩個底線,這樣外部程式就無法取得屬性值,也就無法修改屬性
class Demo2:
__x = 0
def __init__(self, i):
self.__i = i
Demo2.__x += 1
d = Demo2(1)
print(Demo2.__x)
print(d.i)
#《程式語言教學誌》的範例程式
# http://kaiching.org/
# 檔名:encapsulate02.py
# 功能:示範定義類別
# 作者:張凱慶
於命令列執行以上程式,結果如下
$ python3 encapsulate02.py |
Traceback (most recent call last): |
File "encapsulate02.py", line 9, in <module> |
print(Demo2.__x) |
AttributeError: type object 'Demo2' has no attribute '__x' |
$ |
可是如果還是有需要取得屬性值的話要怎麼辦?簡單的方式就是設定 get 方法 (method) ,如此例的 get_x() 及 get_i() 來取得屬性值
class Demo3:
__x = 0
def __init__(self, i):
self.__i = i
Demo3.__x += 1
@classmethod
def get_x(cls):
return cls.__x
def get_i(self):
return self.__i
d = Demo3(1)
print(Demo3.get_x())
print(d.get_i())
#《程式語言教學誌》的範例程式
# http://kaiching.org/
# 檔名:encapsulate03.py
# 功能:示範定義類別
# 作者:張凱慶
於命令列執行以上程式,結果如下
$ python3 encapsulate03.py |
1 |
1 |
$ |
最後來看看封裝在繼承 (inheritance) 後的情況,此例 Demo4 類別繼承 Demo3 類別, Demo3 類別的類別屬性 __x 及實體屬性 __i 都經過封裝,而 Demo4 的實體物件變數 d 依然可以取得 Demo3 類別的類別屬性 __x 及實體屬性 __i ,因此封裝後的屬性仍會透過繼承機制讓子類別 (subclass) 繼承
from encapsulate03 import Demo3
class Demo4(Demo3):
pass
d = Demo4(2)
print(Demo4.get_x())
print(d.get_i())
#《程式語言教學誌》的範例程式
# http://kaiching.org/
# 檔名:encapsulate04.py
# 功能:示範定義類別
# 作者:張凱慶
於命令列執行以上程式,結果如下
$ python3 encapsulate04.py |
1 |
1 |
2 |
2 |
$ |
相關教學影片