首先,property裝飾器是通過資料描述符實現的。用法很簡單,大家應該都知道,這裡就不細說了。

這裡主要分析一下property是如何通過描述符實現的。

class Property:
    def __init__(self, fget):
        self.fget = fget    # 為例項增加方法,這裡的方法是未繫結例項的,不會自動傳入例項self
        self.fset = None    # 同上,未繫結例項

    def __get__(self, instance, owner):
        if instance is not None:
            return self.fget(instance)  # 呼叫原方法,傳入例項self
        return self

    def __set__(self, instance, value):
        self.fset(instance, value)  # 呼叫原方法,傳入例項self和value

    def setter(self, func):
        self.fset = func  # 更新屬性
        return self

class A:
    def __init__(self, data):
        self._data = data

    @Property  # data = Property(data) 描述符例項
    def data(self):
        return self._data

    @data.setter  # data = data.setter(data) 更新屬性,並返回描述符例項
    def data(self, value):
        self._data = value

訪問

a = A(100)
print(a.data)  # 訪問描述符例項,呼叫__get__()方法
# 100

執行過程如下:
1. 在裝飾器中,data變數指向Property(func)描述符例項
2. 原data()函式被賦值給fget變數
3. a.data就是訪問描述符例項,觸發呼叫__get__()方法
4. 最後,呼叫fget()也就是原data()方法,並傳入owner class的例項,亦即呼叫data(self)

賦值

a.data = 200  # 訪問描述符例項,呼叫__set__()方法
print(a.data)  # 訪問描述符例項,呼叫__get__()方法
# 200

執行過程如下:
1. 在裝飾器中,data變數指向data.setter(func)描述符例項。注意,後面的data是__get__()方法返回的描述符例項
2. 原data()函式被賦值給fset變數
3. 賦值操作觸發呼叫__set__方法
4. 最後,呼叫fset也就是原data()方法,並傳入owner class的例項和要賦的值,亦即呼叫data(self, value)

這裡涉及到裝飾器,需要對裝飾器和麵向物件有足夠的瞭解才能弄明白當中的變數傳遞,希望你不會被擋住。

參考:
https://docs.python.org/3/library/functions.html#property