__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__
這些特殊方法了。