属性查找顺序
屬性查詢順序
class A():class_name = "A_name"def __init__(self,obj_name=None):self.obj_name = obj_name查詢類屬性
A.class_name會先從類空間找,如果沒有,再從父類空間找(絕不從對象空間找)
查詢對象屬性(會相對復雜)
放置在最后講
查詢父類空間的所有內容
print(A.__dict__)查詢對象空間的所有內容
print(A().__dict__)私有屬性
1、 對象的私有屬性一般是在對象空間中,在屬性前加雙下劃線__,例如設置為self.__name 2、 對象私有屬性不能通過對象._name這種常規方式獲取,但可以通過對象._類名__屬性名,如`user._User.__name`,類名這種方式來獲取和修改 class A():def __init__(self):self.__name = "obj_name" cat = A() # 對象是不能直接獲取私有屬性的 print(cat.__name) # 會直接報錯!!!!!!! # 獲取私有屬性的方式是:對象._類型__屬性名 print(cat._A__name) # 修改私有屬性 cat._A__name = "changename" print(cat._A__name) # 注意!!!! # 如果這樣設置屬性,這就相當于直接給這個對象生成了私有屬性,其他對象無法訪問, # 但它并不是對象空間里的私有屬性,其他對象不能共享,只有這個對象能擁有 cat.__name = "another name" # 不會報錯,只是cat這個對象會擁有一個另外的__name屬性,與對象空間里的__name不沖突 print(cat.__name) # 打印出來是"another name" !!!!!!!!對象的屬性控制
- 一般使用__getattr__和__setattr__對對象的屬性進行控制
- __getattr__在查找不到屬性時調用,__setattr__是設置屬性,并存入對象的__dict__中
property動態屬性:
1、可獲取,不可設置,
2、可獲取,可設置,但獲取的值與設置的值不同
3、不可獲取,可設置
通過@property裝飾器,將方法設置為屬性,方法內可以是其他操作,但可以像對象調用屬性的形式那樣,來調用被裝飾的方法,如對象.該方法,
- 案例,例如年齡為動態的私有屬性,不能傳遞,只能根據其他屬性如出生年,由內部計算后返回
- 可直接通過對象.方法名獲取,如a.age,但不可設置age(1、可獲取,不可設置)
獲取動態屬性,和設置動態屬性,是矛盾的!!!!
- 如下案例
- 要通過property直接設置動態屬性,(2、不可獲取,可設置)
因此發現,獲取動態屬性和設置動態屬性這兩者之間,是矛盾的,如果同時定義了“獲取”和“設置”這兩個操作,那么獲取所看到的值和設置的值是不一樣的
- 只設置動態屬性,但無法獲取(3、不可獲取,可設置)
- 案例用于一些密碼,設置密碼,卻無法在外部獲取
更深入解析屬性查詢的原理
- __getattr__,__getattribute__,
1、 __getattr__在查找不到屬性時調用,可靈活加邏輯
2、 __getattribute__無論屬性是否存在,都會進入__getattribute__,把持住所有屬性的入口,內部其實會調用__getattr__
- 不建議重寫,除非寫框架
####### 當獲取不到屬性時,__getattr__一般有三個作用
1、作為提示用
2、找尋正確屬性
3、在字典里查找屬性
- 獲取屬性時,無論屬性是否存在,都會進入__getattribute__,把持住所有屬性的入口
但一般不建議自己設置__getattribute__,因為屬性一般的查找順序是這樣的(只有__getattr__和__getattribute__情況下的屬性查找順序)
1、 獲取對象屬性value,先找對象空間里的__dict__字典,obj.__dict__[value]
2、如果對象空間無value屬性,再找類空間里的__dict__字典,type(obj).__dict__[value]
3、 如果類空間無value屬性,則會往上找父類空間的__dict__字典,一直往上找到為止(根據mro的繼承算法)
4、 如果最終找不到,才會去調用基類object中默認的__getattribute__方法
- 如果我們自己定義了__getattribute__方法,極有可能會打亂屬性查找的逐級順序
如果再加上屬性描述符,則屬性的查找順序又會大有不同
屬性描述符:
在python中,實現對象的__get__、__set__或__delete__方法的類稱為描述符
描述符的強大作用:對某些特定屬性進行操作
# 例如,當一個對象有name、age、email屬性時,要求name是字符串、age是int、email是另外一種格式, # 當在類中設置__getattr__、__setattr__、__delattr__、__getattribute__等方法,來實現對屬性值name的字符串判斷設置時,無法統一到其他屬性,如age的整型判斷 # 因此__getattr__、__setattr__更像是對象的所有屬性統一管理控制由于在開發中,有時需要對不同屬性進行不同的管理控制,因此可以將屬性抽象出來,變為一個類,實現__get__、__set__、或__delete__,形成屬性描述符
- 描述符是python中功能強大的通用協議。是屬性、方法、靜態方法、類方法和super()背后的機制
屬性描述符的分類:非數據描述符and數據描述符
1、 非數據描述符:只實現了__get__,只能被讀取
2、 數據描述符:實現了__get__和__set__,可被讀寫,但要注意————設置的屬性并不會存到__dict__中
- 1、非數據描述符:只實現了__get__,沒有__set__的類,只能被讀取
- 2、數據描述符:實現了__get__和__set__兩個魔法函數,可被讀寫
屬性描述符的使用:
以數據描述符作為例子
- 方法1:自定義property中的方法,替換__get__\__set__\__delete__,直接生成屬性描述符,但目前只探索到的功能有限,僅能通過該屬性描述符的對象直接調用描述符里定義的屬性,無法管理多個特定屬性
- 方法2:常規使用__get__\__set__\__delete__,生成屬性描述符后,再創建一個類,在類空間生成屬性描述符的對象,在外部創建類對象后,再進行調用
而最終的屬性查找順序,則是如下
1、查找屬性的第一步是搜索基類列表,即type(b).__mro__,直到找到該屬性的第一個定義,并將該屬性的值賦值給descr;
2、判斷descr的類型。它的類型可分為數據描述符、非數據描述符、普通屬性、未找到等類型。若descr為數據描述符,則調用desc.__get__(b, type(b)),并將結果返回,結束執行。否則進行下一步;
3、如果descr為非數據描述符、普通屬性、未找到等類型,則查找實例b的實例屬性,即b.__dict__。如果找到,則將結果返回,結束執行。否則進行下一步;
4、如果在b.__dict__未找到相關屬性,則重新回到descr值的判斷上。
- 若descr為非數據描述符,則調用desc.get(b, type(b)),并將結果返回,結束執行;
- 若descr為普通屬性,直接返回結果并結束執行;
- 若descr為空(未找到),則最終拋出 AttributeError 異常,結束查找。
總結
- 上一篇: 计算机课程教学与计算科学
- 下一篇: ArcGIS Engine属性查询和空间