為什么不需要為Python對象添加 getter 和 setter


Getter 和 setter在java中被廣泛使用。一個好的java編程准則為:將所有屬性設置為私有的,同時為屬性寫getter和setter函數以供外部使用。 這樣做的好處是屬性的具體實現被隱藏,當未來需要修改時,只需要修改getter 和 setter即可,而不用修改代碼中所有引用這個屬性的地方。可能做的修改為:

  • 在獲取或設置屬性時打一條日志
  • 設置屬性時,對值對進檢查
  • 設置發生時, 修改設置的值
  • 獲取屬性時,動態地計算值

可謂是好處多多,getter和setter為變量訪問提供了靈活的方式。

但python中情況卻不同,因為對象屬性訪問的機制不同。java中需要為變量寫getter和setter的原因為:當我們寫這樣的表達式 person.name 來獲取一個 person 對象的 name 屬性時,這個表達式的意義是固定的,它就是獲取這個屬性,而不可能觸發一個函數的調用。但對於python, 這個表達式即可能是直接獲取一個屬性,也可能會調用一個函數。這取決 Person 類的實現方式。也就是說,python的對象屬性訪問的語法,天然就提供了getter和setter的功能。

由於這個區別,我們沒有必要在python中為每個對象的屬性寫getter和setter。最開始時,我們總是將屬性作為一個直接可訪問的屬性。當后續需要對這個屬性的訪問進行一些控制時,我們可以將其修改為函數觸發式屬性。在修改前后,調用這個對象屬性的代碼不用修改,因為還是使用相同的語法來訪問這個屬性。

可以使用@property 裝飾器將一個直接訪問的屬性轉變為函數觸發式屬性。如下所示,使用@property前的代碼為

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

person = Person("Tom")
print(person.name)

 

代碼的輸出為:

Tom

 

此時為直接訪問 name 這個屬性。當我們需要確保 name 是一個字符串時,可以使用 @property 裝飾器將屬性轉變為一個函數調用,如下所示。

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

    @property
    def name(self):
        print("get name called")
        return self._name

    @name.setter
    def name(self, name):
        print("set name called")
        if not isinstance(name, str):
            raise TypeError("Expected a string")
        self._name = name

person = Person("Tom")
print(person.name)

代碼的輸出為:

set name called
get name called
Tom

可以看出

  • 在創建Person對象時(代碼的倒數第二行), 用於set name的函數被調用。這個函數會檢查輸入是否為一個字符串,如不是則raise一個TypeError
  • 在獲取屬性時(代碼的最后一行),用於get name的函數被調用
  • 在修改前后,使用Person類的代碼完全相同

總結

Python中對象訪問的語法即可能是直接訪問這個屬性,也可能是調用一個函數,這取決於類的實現方式。我們可以在不修改調用者代碼的前提下,輕松切換這兩種方式。可見python原生就提供了添加額外getter和setter所帶來的好處。因此沒有必要一開始就為對象屬性編寫getter和setter函數,而是在需要時切換到函數調用式屬性。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM