Python 封裝(Encapsulation):為什麼不應該讓所有程式都能隨意修改物件資料?
Python 物件導向系列(四)|Class × Inheritance × Polymorphism × Encapsulation
AI 能快速寫出 Python 類別,但真正的設計能力,是知道哪些資料需要檢查、哪些操作應該被允許,以及如何讓物件維持合理的狀態。
前面三篇已介紹類別、繼承與多型。這次繼續以「水井村智慧養殖池」為例,理解封裝(Encapsulation)的用意。封裝不只是把資料藏起來,更是把資料與相關操作組織在一起,提供清楚的存取介面與檢查規則。
一、生活中的封裝:自動販賣機
使用者可以選商品、付款與購買,但不能任意修改販賣機的庫存帳務。操作必須經過規定的介面;程式中的物件也可以如此設計。
二、沒有檢查的公開屬性,會發生什麼事?
class Pond:
def __init__(self, name, oxygen):
self.name = name
self.oxygen = oxygen
pond1 = Pond("水井一號池", 5.8)
pond1.oxygen = -100
print(pond1.oxygen)
執行結果:
-100
溶氧濃度不應為負數,但這段程式仍允許寫入。當感測數據用於警報或控制時,資料錯誤可能引發後續問題。
三、第一步:透過方法更新資料
class Pond:
def __init__(self, name, oxygen):
self.name = name
self.oxygen = 0
self.set_oxygen(oxygen)
def set_oxygen(self, value):
if value >= 0:
self.oxygen = value
else:
print("錯誤:溶氧不能小於 0")
def show_info(self):
print(self.name, "溶氧:", self.oxygen)
pond1 = Pond("水井一號池", 5.8)
pond1.set_oxygen(6.2)
pond1.set_oxygen(-100)
pond1.show_info()
錯誤:溶氧不能小於 0
水井一號池 溶氧: 6.2
這個版本可以檢查經過 set_oxygen() 的更新,但外部仍能執行 pond1.oxygen = -100 繞過檢查,因此需要更清楚的屬性介面。
四、Python 的屬性命名與封裝
| 寫法 | 意義 | 注意事項 |
|---|---|---|
self.oxygen | 公開屬性 | 通常可直接讀寫 |
self._oxygen | 非公開慣例 | 提醒使用者避免從外部直接修改 |
self.__oxygen | 名稱改寫 | 降低意外存取及子類別名稱衝突,並非絕對私有 |
Python 的雙底線屬性會發生 Name Mangling(名稱改寫),例如 __oxygen 可能成為 _Pond__oxygen。這不是安全邊界,也不能用來防止惡意程式存取。
五、推薦實作:使用 @property 驗證資料
class Pond:
def __init__(self, name, oxygen):
self.name = name
self.oxygen = oxygen
@property
def oxygen(self):
return self._oxygen
@oxygen.setter
def oxygen(self, value):
if value < 0:
raise ValueError("溶氧不能小於 0")
self._oxygen = value
pond1 = Pond("水井一號池", 5.8)
print(pond1.oxygen)
pond1.oxygen = 6.2
print(pond1.oxygen)
5.8
6.2
此時 pond1.oxygen = 6.2 看似直接指定屬性,實際上會呼叫 @oxygen.setter 的檢查邏輯。若執行 pond1.oxygen = -100,則會引發 ValueError。
_oxygen 仍可被直接存取。封裝的目的是建立可靠、清楚的操作介面,並非提供資訊安全上的絕對保護。六、封裝流程圖:資料如何被檢查?
使用者只需要知道如何操作公開介面,物件內部則負責檢查與維持資料一致性。
七、所有屬性都要用 Getter 與 Setter 嗎?
不需要。如果只是簡單的學生姓名,直接使用公開屬性就很合理:
class Student:
def __init__(self, name):
self.name = name
只有在需要驗證、轉換、唯讀控制或維持特殊規則時,才考慮 @property 或明確的操作方法。好的封裝不是讓程式變長,而是讓規則更明確。
八、No-AI → AI Pair → Challenge AI 實習
No-AI:先發現資料問題
class Product:
def __init__(self, name, stock):
self.name = name
self.stock = stock
product = Product("水井文蛤", 100)
product.stock = -50請思考:負數庫存有什麼問題?如果直接修改庫存,哪些規則可能被跳過?
AI Pair:與 AI 合作改善
建議提示詞:
請使用 Python 的 @property 改善 Product 類別,讓庫存不能小於零,並提供購買商品的方法。請比較公開屬性與 property 的優缺點,程式保持適合大學生理解。
拿到 AI 程式後,請測試:負數庫存、庫存不足、購買零件商品及正常購買。
Challenge AI:需求突然改變
新規則:「庫存不允許由外部直接設定,只能透過進貨 restock() 與銷售 purchase() 改變。」請設計唯讀的 stock 屬性,並判斷這樣是否比公開 Setter 更符合需求。
九、物件導向四個重要觀念比較
| 觀念 | 核心問題 | 智慧養殖範例 |
|---|---|---|
| Class 類別 | 如何組織資料與行為? | 建立 Pond |
| Inheritance 繼承 | 如何共用與擴充既有設計? | SmartPond(Pond) |
| Polymorphism 多型 | 如何用相同方式操作不同物件? | check_status() |
| Encapsulation 封裝 | 如何管理資料存取並維持合理狀態? | oxygen 的驗證 |
十、結語:AI 會寫封裝,但誰來決定規則?
AI 可以快速產生 @property、Getter 與 Setter,但只有理解應用情境的人,才能判斷哪些資料不合理、哪些操作應被允許,以及哪些規則需要由物件自己維護。
AI 協助寫程式;學生負責理解問題、設計規則、驗證行為。這是 AI 時代學習 Python 物件導向的重要能力。
沒有留言:
張貼留言