__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__ 在尋找屬性時的順序是:

  1. 如果有定義 __getattribute__,傳回 __getattribute__ 的值。
  2. 在產生實例的類別 __dict__ 尋找是否有相符的屬性名稱。如果找到且實際是個資料描述器,傳回 __get__ 方法的值。如果是個非資料描述器,進行第 3 步。
  3. 在實例的 __dict__ 尋找是否有相符的屬性名稱,如果有則傳回值。
  4. 在產生實例的類別 __dict__ 尋找是否有相符的屬性名稱。如果不是描述器,直接傳回屬性值。如果是個描述器,傳回 __get__ 的值。
  5. 如果實例有定義 __getattr__,就視 __getattr__ 如何處理,若沒有定義 __getattr__,會丟出 AttributeError

簡單來說,取得屬性的順序之記憶原則為:實例的 __getattribute__、資料描述器的 __get__、實例的 __dict__、非資料描述器的 __get__、實例的 __getattr__

那麼 __getattribute____getattr__ 的差別是?就算 __dict__ 中存在屬性,最先執行的也是 __getattribute__,它經常用來作為取得屬性時最前端的攔截器(interceptor);而由於實例 __dict__ 中存在屬性時就會傳回值,__getattr__ 可作為前面過程都找不到屬性時的最後一道防線。

__setattr__、__delattr__

__setattr__ 的作用,在於攔截所有對實例的屬性設定。如果對實例有個設定屬性的動作,設定的順序如下:

  1. 如果有定義 __setattr__ 就呼叫,沒有進行下一步。
  2. 在產生實例的類別上,看看 __dict__ 是否有相符合的屬性名稱。如果找到且實際是個資料描述器,呼叫描述器的 __set__ 方法,如果不是,進行下一步。
  3. 在實例的 __dict__ 上設定屬性與值。

簡單來說,設定屬性順序記憶的原則是:實例的 __setattr__、資料描述器的 __set__、實例的 __dict__

__delattr__ 的作用,在於攔截所有對實例的屬性刪除。如果對實例有個刪除屬性的動作,刪除的順序如下:

  1. 如果有定義 __delattr__ 則呼叫,如果沒有進行下一步。
  2. 在產生實例的類別上,看看 __dict__ 是否有相符合的屬性名稱。如果找到且實際是個資料描述器,呼叫描述器的 __delete__ 方法,如果不是資料描述器,進行下一步。
  3. 在實例的 __dict__ 找看看有無相符合的屬性名稱,如果有就刪除,如果沒有會丟出 AttributeError

簡單來說,刪除屬性順序記憶的原則是:實例的 __delattr__、資料描述器的 __delete__、實例的 __dict__

就算 __dict__ 中存在屬性,__setattr____delattr__ 一定會先呼叫,也就不存在 __setattribute____delattribute__ 這些特殊方法了。

分享到 LinkedIn 分享到 Facebook 分享到 Twitter