1、描述符的定義 描述符是與特定屬性互相綁定的一種協議,通過方法被觸發修改屬性,這些方法包括__get__(),__set__(),__delete__().將這些方法定義在類中,即可實現描述符 2、屬性與__dict__ Python中類有屬於自己的字典屬性,經過類的實例化的對象也同樣有自己的字典 ...
1、描述符的定義
描述符是與特定屬性互相綁定的一種協議,通過方法被觸發修改屬性,這些方法包括__get__(),__set__(),__delete__().將這些方法定義在類中,即可實現描述符
2、屬性與__dict__
Python中類有屬於自己的字典屬性,經過類的實例化的對象也同樣有自己的字典屬性,__dict__
1 class Foo(object): 2 x=10 3 def f(self): 4 print('f') 5 def __init__(self,name,id): 6 self.name=name 7 self.id=id 8 f=Foo('alex','1001') 9 print(Foo.__dict__) 10 print(f.__dict__) 11 {'__module__': '__main__', 'x': 10, 'f': <function Foo.f at 0x000002677119A950>, '__init__': <function Foo.__init__ at 0x000002677119A840>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None} 12 {'name': 'alex', 'id': '1001'}
對象調用屬性的方法查找順序:對象字典,類字典,父類字典,__getattr__方法
3、描述符方法
__set__(self,instance,value)
__get__(self,instance,owner)
__delete__(self,instance)
#測試self,instance,value,owner各是何方神聖
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 print(self) 5 print(instance,value) 6 def __get__(self, instance, owner): 7 print('get') 8 print(self) 9 print(instance,owner) 10 def __delete__(self, instance): 11 print('delete') 12 print(self) 13 print(instance) 14 class Test: 15 x=Foo() 16 def __init__(self,y): 17 self.x=y 18 t=Test(10) 19 t.x
#輸出結果 20 set 21 <__main__.Foo object at 0x000002D26D10A438> 22 <__main__.Test object at 0x000002D26D10A470> 10 23 get 24 <__main__.Foo object at 0x000002D26D10A438> 25 <__main__.Test object at 0x000002D26D10A470> <class '__main__.Test'>
第15行x=Foo()說明x屬性被Foo類所代理一般,涉及對x屬性的操作可能會觸發Foo類中的三個方法,t為Test實例化的對象,觸發構造方法init,執行self.x=y(10),實際類屬性與實例新增屬性x是井水不犯河水,無相關聯,但是類屬性x是描述符屬性,被Foo代理,python解釋器會發現實例字典中的x屬性名與類屬性同名,類屬性(描述符)會優先覆蓋。對x的操作交給Foo()代理,觸發其中的set函數,列印其中self——類Foo類的信息,instance——被代理的對象信息,value——被代理的值被修改。
第19行對x的屬性訪問,理所應當,觸發其代理的get方法,self——類Foo類的信息,instance——被代理的對象信息,owner——見名知意,被代理屬性的最高擁有著,即Test類
其實當一個類中定義了set,get,delete的一個或多個,就可以把這個類稱為描述符類。當沒有set方法,有其他2個任意或所有時,又被稱為非數據描述符。至少有get和set,稱為數據描述符
4、描述符對象是實例屬性
從上述可知描述符對象是類屬性。當描述符對象是實例屬性又會怎麼樣呢?
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 print(instance,value) 5 def __get__(self, instance, owner): 6 print('get') 7 print(instance,owner) 8 def __delete__(self, instance): 9 print('delete') 10 print(instance) 11 class Test: 12 x=Foo() 13 def __init__(self): 14 self.y=Foo() 15 t=Test() 16 t.x 17 t.y
#輸出 18 get 19 <__main__.Test object at 0x00000175F2FABF28> <class '__main__.Test'>
咦?為什麼只觸發了一個get。t.y並沒有觸發get方法。why???
因為調用 t.y 時,首先會去調用Test(即Owner)的 __getattribute__() 方法,該方法將 t.y 轉化為Test.__dict__['y'].__get__(t, Test), 但是呢,實際上 Test 並沒有 y這個屬性,y 是屬於實例對象的,so,忽略。
5、類描述符對象屬性與實例描述符對象屬性同名
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 print(instance,value) 5 def __get__(self, instance, owner): 6 print('get') 7 print(instance,owner) 8 def __delete__(self, instance): 9 print('delete') 10 print(instance) 11 class Test: 12 x=Foo() 13 def __init__(self): 14 self.x=Foo() 15 t=Test() 16 t.x 17 ----------------------------------------------------- 18 get 19 <__main__.Test object at 0x00000246E4ACBF28> <class '__main__.Test'>
大家應該會想,實例屬性通過__getattribute__()已經在自己字典中可以找到x,為什麼還會觸發get方法?
這涉及到優先順序的順序問題,當解釋器發現實例字典中有與描述符屬性同名的屬性時,描述符優先與實例屬性,會覆蓋掉實例屬性。可以通過類字典驗證
1 print(Test.__dict__) 2 ------------- 3 {'__module__': '__main__', 'x': <__main__.Foo object at 0x000002757C138550>, '__init__': <function Test.__init__ at 0x000002757C1DA9D8>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
x所對應的value值是其本類對象,而t.__dict__則是個空字典。
6、描述符優先順序別順序
上面已經提到,當無set方法的描述符稱為非數據描述符,有set和get為數據描述符。這2者有啥區別?優先順序別的大區別!!!
類屬性>>>數據描述符>>>實例屬性>>>>非數據屬性>>>>找不到此屬性即__getattribute__`