python之屬性描述符與屬性查詢規則
描述符
import numbers class IntgerField: def __get__(self, isinstance, owner): print('獲取age') return self.num def __set__(self, instance, value): print('設定age值時') if not isinstance(value, numbers.Integral): raise ValueError('int need') self.num = value def __delete__(self, instance): pass class User: age = IntgerField() // 資料屬性描述符,查詢優先順序最高 user = User() user.age = 33 print(user.age)
上述的User可以看做資料庫中的表,假設我們要控制user中age的賦值型別,固然可以使用以下形式進行攔截:
class User: age = 33 def __setattr__(self, name, value): pass // 這裡進行型別檢測 又或者: class User: @property def age(self): return self._num @age.setter def age(self,value): self._num = value // 這裡進行型別檢測
但是這種一兩次還行,多了就是在寫重複程式碼,所以就可用上述類IntgerField中定義__get__,__set__等實現屬性描述符的方式進行攔截。
getattribute、getattr、setattr、__delattr__等方法用來實現屬性查詢、設定、刪除的一般邏輯,而對屬性的控制行為就由屬性物件來控制。這裡單獨抽離出來一個屬性物件,在屬性物件中定義這個屬性的查詢、設定、刪除行為。這個屬性物件就是描述符。
描述符物件一般是作為其他類物件的屬性而存在。在其內部定義了三個方法用來實現屬性物件的查詢、設定、刪除行為。這三個方法分別是:
get(self, instance, owner):定義當試圖取出描述符的值時的行為。
set(self, instance, value):定義當描述符的值改變時的行為。
delete(self, instance):定義當描述符的值被刪除時的行為。
其中:instance為把描述符物件作為屬性的物件例項;
owner為instance的類物件。
描述符有資料描述符和非資料描述符之分
只要至少實現__get__、set、__delete__方法中的一個就可以認為是描述符;
只實現__get__方法的物件是非資料描述符,意味著在初始化之後它們只能被讀取;
同時實現__get__和__set__的物件是資料描述符,意味著這種屬性是可讀寫的。
屬性查詢規則
當查詢物件上的某個屬性時,假設是user.age,順序先是判斷該例項所指向的類以及基類的__dict__中查詢,並且如果該屬性資料屬性描述符,就會呼叫描述符中的__get__方法
如果該age直接出現在obj.__dict__上,直接返回obj.__dict__['age']
如果該屬性出現在User中或基類中,且該屬性是非資料屬性描述符,就會呼叫其__get__方法,如果不是非資料屬性描述符,就會呼叫User或基類的User或基類.__dict__['age']
如果這些都沒有,且User上也沒有__getattr__方法,就會報錯