Python 類別進階:@property, @classmethod 與 @staticmethod (第 18 章)

站主自己的課程,請大家支持
揭秘站長的架站心法:如何利用 Hugo × AI 打造高質感個人品牌網站? 揭秘站長的架站心法:如何利用 Hugo × AI 打造高質感個人品牌網站?
  • Post by
  • Jan 01, 2024
post-thumb

在 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:靜態方法

完全不需要 selfcls 參數。它就像是一個普通的函數,只是剛好被放在類別裡面 (通常為了組織代碼,因為這個函數跟類別邏輯相關)。

class MathUtils:
    @staticmethod
    def add(x, y):
        return x + y

print(MathUtils.add(5, 3)) # 8

注意:如果不確定要用 @classmethod 還是 @staticmethod,問自己:「這個方法需要存取類別屬性嗎?」如果需要,用 classmethod;如果完全獨立,用 staticmethod

5. 比較總結

裝飾器第一個參數用途存取權限
無 (Instance Method)self操作物件狀態能存取物件與類別屬性
@classmethodclsAlternative Constructor (工廠)只能存取類別屬性
@staticmethod工具函數 (Utility)無法存取物件或類別屬性
@propertyselfGetter/Setter 封裝能存取物件與類別屬性

6. 結論

善用這些裝飾器,可以讓你的類別設計更語意化 (Semantic)。

  • @property 保護數據完整性。
  • @classmethod 提供多種實例化方式。
  • @staticmethod 收納相關的工具函數。

下一章,我們將介紹如何優雅地管理資源 (如檔案開啟、資料庫連線),進入 Context Managers 的世界!


延伸閱讀

LATEST POST
TAG