Skip to content

[Python] @property 裝飾器將類別方法轉為只讀屬性

Last Updated on 2024-08-27 by Clay

在 Python 的類別建構中,@property 是一種經常見到且確實有其好處的裝飾器,它主要的用途在於將類別的方法class method)轉為只讀(read-only)屬性attribute),讓使用者透過屬性呼叫的方式來取得計算結果。

另一個優點則是可以使程式碼在閱讀或維護時更加直觀,可以隱去一些計算細節。


使用方式

以下我們來看一個轉換時間戳(timestamp)為日期字串的類別,其中每個類別方法都返回不同格式的日期;只要我們修改了類別的時間戳,就可以在呼叫對應的 @property 類別方法時,看到被轉換好的結果。(而且使用方式跟類別屬性一樣

import datetime


class TimestampFormatter:
    def __init__(self, timestamp):
        self._timestamp = timestamp

    @property
    def date_slash(self):
        """ Return format yyyy/mm/dd """
        date = datetime.datetime.fromtimestamp(self._timestamp)
        return date.strftime('%Y/%m/%d')

    @property
    def date_dash(self):
        """ Return format yyyy-mm-dd """
        date = datetime.datetime.fromtimestamp(self._timestamp)
        return date.strftime('%Y-%m-%d')

    @property
    def date_dot(self):
        """ Return format yyyy.mm.dd """
        date = datetime.datetime.fromtimestamp(self._timestamp)
        return date.strftime('%Y.%m.%d')

    @property
    def date_space(self):
        """ Return format yyyy mm dd """
        date = datetime.datetime.fromtimestamp(self._timestamp)
        return date.strftime('%Y %m %d')


if __name__ == "__main__":
    current_timestamp = datetime.datetime.now().timestamp()

    # Create TimestampFormatter instance
    formatter = TimestampFormatter(int(current_timestamp))

    # Print different format
    print("Date with slashes:", formatter.date_slash)
    print("Date with dashes:", formatter.date_dash)
    print("Date with dots:", formatter.date_dot)
    print("Date with spaces:", formatter.date_space)


Output:

Date with slashes: 2024/08/27
Date with dashes: 2024-08-27
Date with dots: 2024.08.27
Date with spaces: 2024 08 27

直覺上,這樣有點類似我們設置好類別實體的一個屬性值,其他屬性值也會與之相對改變,在類別不同屬性之間彼此有關聯的情況下這樣寫特別直覺。


擴展使用

Setter 方法

setter 方法是 @property 裝飾器的一個使用方法,用於設置或修改屬性的值。當我們需要在替類別的屬性賦值時進行額外的檢查或轉換,例如數據驗證或自動格式化,setter 方法是一個理想的選擇。

基本用法

我們可以透過在方法名前加上 @<property_name>.setter 來定義 setter(我一開始其實看不懂 <property_name> 的命名是哪裡來的,後來才發現是我們自己定義的方法),以下是一個範例:

class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError("Name must be a string")
        self._name = value


在這個例子中,當嘗試設置 name 屬性時,setter 方法會先檢查新值是否為字符串。如果不是,將拋出一個 TypeError


Deleter 方法

deleter 方法用於定義當一個屬性被刪除時需要進行的操作,例如清理或釋放資源。這在處理文件或數據庫連接時尤為重要。

基本用法

deleter 可以通過在方法名前加上 @<property_name>.deleter 來定義。以下是一個範例:

class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.deleter
    def name(self):
        print("Deleting name...")
        del self._name


del 操作被用於 name 屬性時,會調用這個 deleter 方法,進行額外的日誌記錄或其他清理操作。


總結來說,使用 @property 有著以下好處:

  • 異常處理:在 setterdeleter 方法中添加異常處理,可以防止不正確的使用方式對物件造成損害
  • 依賴屬性:當某個屬性的值取決於其他屬性時,可以使用 @property 來確保所有相關屬性都是同步更新的
  • 暫存計算值:對於計算成本高的屬性,可以在第一次計算後將結果暫存起來,後續訪問直接返回緩存值,直到相依的資料發生變化

References


Read More

Tags:

Leave a Reply