__getattribute__、__getattr__、__setattr__、__delattr__
May 11, 2022類別可以定義 __getattribute__、__getattr__、__setattr__、__delattr__ 等方法,以決定存取實例屬性時的行為:
def __getattribute__(self, name)
def __getattr__(self, name)
def __setattr__(self, name, value)
def __delattr__(self, name)
__getattribute__、__getattr__
__getattribute__ 最容易解釋,定義了這個方法,任何屬性的尋找都會被攔截(包含 __xxx__ 內建屬性名稱)。
__getattr__ 的作用,是作為尋找屬性的最後一個機會。如果同時定義有 __getattribute__、__getattr__ 在尋找屬性時的順序是:
- 如果有定義
__getattribute__,傳回__getattribute__的值。 - 在產生實例的類別
__dict__尋找是否有相符的屬性名稱。如果找到且實際是個資料描述器,傳回__get__方法的值。如果是個非資料描述器,進行第 3 步。 - 在實例的
__dict__尋找是否有相符的屬性名稱,如果有則傳回值。 - 在產生實例的類別
__dict__尋找是否有相符的屬性名稱。如果不是描述器,直接傳回屬性值。如果是個描述器,傳回__get__的值。 - 如果實例有定義
__getattr__,就視__getattr__如何處理,若沒有定義__getattr__,會丟出AttributeError。
簡單來說,取得屬性的順序之記憶原則為:實例的 __getattribute__、資料描述器的 __get__、實例的 __dict__、非資料描述器的 __get__、實例的 __getattr__。
那麼 __getattribute__ 與 __getattr__ 的差別是?就算 __dict__ 中存在屬性,最先執行的也是 __getattribute__,它經常用來作為取得屬性時最前端的攔截器(interceptor);而由於實例 __dict__ 中存在屬性時就會傳回值,__getattr__ 可作為前面過程都找不到屬性時的最後一道防線。
__setattr__、__delattr__
__setattr__ 的作用,在於攔截所有對實例的屬性設定。如果對實例有個設定屬性的動作,設定的順序如下:
- 如果有定義
__setattr__就呼叫,沒有進行下一步。 - 在產生實例的類別上,看看
__dict__是否有相符合的屬性名稱。如果找到且實際是個資料描述器,呼叫描述器的__set__方法,如果不是,進行下一步。 - 在實例的
__dict__上設定屬性與值。
簡單來說,設定屬性順序記憶的原則是:實例的 __setattr__、資料描述器的 __set__、實例的 __dict__。
__delattr__ 的作用,在於攔截所有對實例的屬性刪除。如果對實例有個刪除屬性的動作,刪除的順序如下:
- 如果有定義
__delattr__則呼叫,如果沒有進行下一步。 - 在產生實例的類別上,看看
__dict__是否有相符合的屬性名稱。如果找到且實際是個資料描述器,呼叫描述器的__delete__方法,如果不是資料描述器,進行下一步。 - 在實例的
__dict__找看看有無相符合的屬性名稱,如果有就刪除,如果沒有會丟出AttributeError。
簡單來說,刪除屬性順序記憶的原則是:實例的 __delattr__、資料描述器的 __delete__、實例的 __dict__。
就算 __dict__ 中存在屬性,__setattr__、__delattr__ 一定會先呼叫,也就不存在 __setattribute__、__delattribute__ 這些特殊方法了。


