
在 Java 或 C++ 中,我們習慣用 private 變數加上 getVariable() 和 setVariable() 來封裝數據。但在 Python 中,這太不優雅了!
Python 提供了更 Pythonic 的方式來管理類別成員:內建裝飾器。
1. @property:優雅的封裝
假設我們有一個 Circle 類別,半徑 radius 不應該是負數。
class Circle:
def __init__(self, radius):
self._radius = radius # 前面加底線表示 "內部變數" (慣例,非強制)
@property
def radius(self):
"""Getter: 像存取變數一樣呼叫"""
return self._radius
@radius.setter
def radius(self, value):
"""Setter: 設定變數時自動檢查"""
if value < 0:
raise ValueError("半徑不能為負數")
self._radius = value
@property
def area(self):
"""Computed Property: 計算屬性 (唯讀)"""
return 3.14 * self._radius ** 2
c = Circle(5)
print(c.radius) # 5 (呼叫 getter)
c.radius = 10 # (呼叫 setter)
print(c.area) # 314.0 (呼叫 area getter)
# c.radius = -1 # ValueError: 半徑不能為負數
# c.area = 100 # AttributeError: can't set attribute (因為沒定義 setter)
使用 @property,我們可以讓方法 (Method) 偽裝成屬性 (Attribute),這讓我們能在不改變外部呼叫代碼 (c.radius) 的情況下,隨時加入邏輯檢查。
2. 實例方法 (Instance Method)
這是我們最熟知的方法,第一個參數必須是 self,代表「物件實例本身」。
class MyClass:
def method(self):
return "Instance method called", self
3. @classmethod:類別方法
第一個參數是 cls,代表「類別本身」,而不是物件實例。
最常見的用途是製作 Factory Method (工廠模式),提供 __init__ 以外的建構方式。
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str):
# 輸入格式 "2024-01-01"
year, month, day = map(int, date_str.split('-'))
# 這裡的 cls(...) 等同於 Date(...)
# 使用 cls 的好處是繼承時會自動對應到子類別
return cls(year, month, day)
# 一般建構
d1 = Date(2024, 1, 1)
# 工廠建構
d2 = Date.from_string("2024-01-01")
print(d2.year) # 2024
4. @staticmethod:靜態方法
完全不需要 self 或 cls 參數。它就像是一個普通的函數,只是剛好被放在類別裡面 (通常為了組織代碼,因為這個函數跟類別邏輯相關)。
class MathUtils:
@staticmethod
def add(x, y):
return x + y
print(MathUtils.add(5, 3)) # 8
注意:如果不確定要用
@classmethod還是@staticmethod,問自己:「這個方法需要存取類別屬性嗎?」如果需要,用classmethod;如果完全獨立,用staticmethod。
5. 比較總結
| 裝飾器 | 第一個參數 | 用途 | 存取權限 |
|---|---|---|---|
| 無 (Instance Method) | self | 操作物件狀態 | 能存取物件與類別屬性 |
| @classmethod | cls | Alternative Constructor (工廠) | 只能存取類別屬性 |
| @staticmethod | 無 | 工具函數 (Utility) | 無法存取物件或類別屬性 |
| @property | self | Getter/Setter 封裝 | 能存取物件與類別屬性 |
6. 結論
善用這些裝飾器,可以讓你的類別設計更語意化 (Semantic)。
- 用
@property保護數據完整性。 - 用
@classmethod提供多種實例化方式。 - 用
@staticmethod收納相關的工具函數。
下一章,我們將介紹如何優雅地管理資源 (如檔案開啟、資料庫連線),進入 Context Managers 的世界!
延伸閱讀: