描述器
描述器的表現
- 用到3個魔術方法:__get__()、__set__()、__delete__()
- 方法用法:
- object.__get__(self,instance,owner)
- object.__set__(self,instance,value)
- object.__delete__(self,instance)
- self指代當前實例,調用者
- instance是owner的實例
- owner是屬性所屬的類
- 看下面代碼,思考執行流程。
- 結論:
- 類加載的時候,類變量需要先生成,而類B的x屬性是類A的實例,所以類A先初始化,所以打印A.init
- 然后執行到B.x.a1.然后打印實例化并初始化B的實例b
- 打印b.x.a1,會查找屬性b.x,指向A的實例,所以返回A實例的屬性a1的值
- 因為定義了__get__方法,類A就是一個描述器,對類B或者類B的實例的x屬性讀取,成為對類A的實例的訪問,就會調用get方法
- 解決上面方法中B.x.a1拋錯問題,報錯是因為添加get方法后造成的,get三個參數的的含義:
- slef都是A的實例
- owner都是B類
- instance說明:
- None表示沒有B類的實例,對應調用B.x
- <__main__.B object at 0x000000310E738828>表示時B的實例,對應調用B().x
- 使用返回值解決,返回self,就是A的實例,該實例有a1屬性,返回正常
結論:只有類屬性是類的實例才可以觸發get
描述器定義
- Python中,一個類實現了__get__、__set__、__delete__三個方法中的任何一個方法,就是描述器
- 如果僅實現了__get__,就是非數據描述符non-data descriptor
- 同時實現了__get__、__set__就是數據描述符data descriptor
- 屬性的訪問順序
- b.x訪問到了實例的屬性,而不是描述器
- 下面代碼為A類增加set方法
結論:屬性查找順序:實例的__dict__優先于非數據描述器數據描述器 優先于實例的__dict____delete__方法有同樣的效果,有了這個方法,就是數據描述器
- 增加b.x = 500,這是調用數據描述器的__set__方法,或調用那個非數據描述器的實例覆蓋
- B.x = 600,賦值即定義,這是覆蓋類的屬性。類的字典更新
本質(進階)
- python真的會做這么復雜嗎,再來一套屬性查找順序規則?看看非數據描述器和數據描述器,類B及其__dict__的變化
- 屏蔽和不屏蔽__set__方法,看看變化
- 原來不是什么數據描述器優先級高,而是把實例的屬性從__dict__中去掉,造成了該屬性如果是數據描述器優先訪問的假象
- 說到底,屬性訪問順序從來沒有變過
Python中的描述器
- 描述器在python中廣泛應用
- python的方法(包括staticmethod()和classmethod())都實現為非數據描述器,因此,實例可以重新定義和覆蓋方法。這允許單個實例獲取與同一類的其他實例不同的行為?
- property()函數實現為一個數據描述器,因此,實例不能覆蓋屬性的行為。
- foo、bar都可以在實例中覆蓋,z不可以
- A.clsmtd()的意思就是None(),一定報錯,如何改?
- A.clsmtd()其實應該是A.clsmtd(cls)(),應該怎么處理?
- A.clsmetd = A.clsmtd(cls)
- 用partial函數
- 對實例的數據進行校驗
- 對上面類的實例的屬性,name、age進行數據校驗
- 方法一:寫函數,在init中檢查如果不合格,直接拋異常
- 方法二:裝飾器,使用inspect模塊完成
- 方法三描述器
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/89076