第29條 用純屬性@property取代get和set方法

在Java等語言的屬性訪問和設置中,通常會在類的代碼中添加gettersetter方法,以實現屬性的設置與獲取。

class OldResistor(object):
    def __init__(self,ohms):
        self._ohms = ohms
    def get_ohms(self):
        return self._ohms
    def set_ohms(self,ohms):
        self._ohms = ohms
        
if __name__ == '__main__':
    r = OldResistor(100)
    print('Before:%s'%r.get_ohms())
    r.set_ohms(200)
    print("After:%s"%r.get_ohms())

這種gettersetter形式對於Python來說較爲繁瑣,因爲每次獲取和設置屬性值都要通過函數來實現。

對於Python來說,基本上不需要手工設置settergetter方法,可以將屬性的訪問權限設置爲public

class Resistor(object):
    def __init__(self,ohms):
        self.ohms = ohms
        self.voltage = 0
        self.current = 0
        
 r1 = Resistor(50e3)
 r1.ohms = 10e3

這樣簡單的public屬性實現Resistor類後,原地自增操作實現變得簡單爲:r1.ohms += 5e3

@property

或者使用@property修飾器結合setter方法來做。
下面這個類繼承自Resistor,同時增加了修改current屬性,此時我們就可以直接訪問並修改:

class Resistor(object):
    def __init__(self,ohms):
        self.ohms = ohms
        self.voltage = 0
        self.current = 0
        
class VoltageResistance(Resistor):
    def __init__(self,ohms):
        super().__init__(ohms)
        self._voltage = 0
    @property
    def voltage(self):
        return self._voltage
    @voltage.setter
    def voltage(self,voltage):
        self._voltage = voltage
        self.current = self._voltage / self.ohms
        
if __name__ == '__main__':
    r1 = VoltageResistance(100)
    print(r1.voltage)
    r1.voltage = 10
    print(r1.voltage)

與此同時,爲屬性設置setter方法時,可以在方法裏面做類型驗證及數值驗證。

class BoundedResistance(Resistor):
    def __init__(self,ohms) -> None:
        super().__init__(ohms)
    @property
    def ohms(self):
        return self._ohms
    @ohms.setter
    def ohms(self,ohms):
        if ohms<=0:###數值驗證
            raise ValueError('%f ohms must be > 0'%ohms)
        self._ohms = ohms

如果此時傳入無效的值,那麼程序便會報錯。

再者,我們可以使用@property來防止父類的屬性遭到修改。

class FixedResistance(Resistor):
    #...
    @property
    def ohms(self):
        return self._ohms
    @ohms.setter
    def ohms(self,ohms):
        if hasattr(self,'_ohms'): ##驗證
            raise AttributeError("Can't set attribute")
        self._ohms = ohms

構建好對象後,如果試圖修改ohms屬性,那麼就會引發異常。

需要注意的是:如果只是單純地設置@property,沒有設置setter那麼該屬性只讀,兩個都設置了,才能讀寫

總結:其實本質上要想對類的屬性進行訪問或設置,完全可以將屬性設置爲public(但是沒法實現相應的驗證邏輯),或者編寫getterr和setter方法,但是gettersetter方法每次調用時,都要先調用函數再調用屬性,比較麻煩,簡便的方法就是使用@property修改屬性函數,這樣既可以通過直接訪問屬性的方式進行修改,同時也可以編寫驗證邏輯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章