日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

属性查找顺序

發布時間:2024/3/26 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 属性查找顺序 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

屬性查詢順序

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__中
```python class User():def __init__(self):self.name = "lili"def __getattr__(self, item):raise AttributeError# 實際上,__setattr__本身是默認就有的,即使不實現__setattr__,屬性也會自動創建并插入對象的__dict__中def __setattr__(self, key, value):print("設置屬性")# self.key = value # 會報錯!!!!不能使用對象.屬性,會導致重復調用__setattr__引起報錯super(User,self).__setattr__(key,value) user = User() user.age = 100 print(user.__dict__)
property動態屬性:

1、可獲取,不可設置,
2、可獲取,可設置,但獲取的值與設置的值不同
3、不可獲取,可設置

通過@property裝飾器,將方法設置為屬性,方法內可以是其他操作,但可以像對象調用屬性的形式那樣,來調用被裝飾的方法,如對象.該方法,
  • 案例,例如年齡為動態的私有屬性,不能傳遞,只能根據其他屬性如出生年,由內部計算后返回
  • 可直接通過對象.方法名獲取,如a.age,但不可設置age(1、可獲取,不可設置)
from datetime import date,datetime class A():def __init__(self,year):self.year = year@propertydef age(self):return datetime.now().year - self.yeara = A(1995) print(a.age) # 輸出27 a.age = 100 # 這里會報錯!!!因為沒有設置的方法
獲取動態屬性,和設置動態屬性,是矛盾的!!!!
  • 如下案例
  • 要通過property直接設置動態屬性,(2、不可獲取,可設置)
from datetime import date,datetime class A():def __init__(self,year):self.year = yearself.__age = 0# 獲取動態屬性@propertydef age(self):return datetime.now().year - self.year# 設置動態屬性@age.setterdef age(self,value):self.__age = value a = A(1995) print(a.age) # 輸出27 a.age = 100 # 設置成功,內部的私有屬性__age會變為100 print(a.age) # 輸出的卻還是27,因為這里是獲取動態屬性,得到的是property裝飾的age()返回值 print(a._A__age) # 輸出的是100,這里私有屬性已經成功改變了

因此發現,獲取動態屬性和設置動態屬性這兩者之間,是矛盾的,如果同時定義了“獲取”和“設置”這兩個操作,那么獲取所看到的值和設置的值是不一樣的

  • 只設置動態屬性,但無法獲取(3、不可獲取,可設置)
  • 案例用于一些密碼,設置密碼,卻無法在外部獲取
import hashlib class User():def __init__(self):self.password_hash = ""def generate_password(self,value):# 根據password生成hash序列m = hashlib.md5() # 生成md5算法對象m.update(value.encode('utf8'))return m.hexdigest() # 返回16進制hash串# 獲取動態屬性就會報錯@propertydef password(self):raise AttributeError("password is not a readable attribute")# 設置password的值,但存在內部的是password_hash,這樣即使獲取了password_hash,也無法解密@password.setterdef password(self,password):self.password_hash = self.generate_password(password)user = User() user.password = "aaaaaaa" print(user.password_hash) print(user.password) # 會報錯!!!!因為獲取動態屬性會直接raise AttributeError
更深入解析屬性查詢的原理
  • __getattr__,__getattribute__,
    1、 __getattr__在查找不到屬性時調用,可靈活加邏輯
    2、 __getattribute__無論屬性是否存在,都會進入__getattribute__,把持住所有屬性的入口,內部其實會調用__getattr__
    - 不建議重寫,除非寫框架

####### 當獲取不到屬性時,__getattr__一般有三個作用
1、作為提示用
2、找尋正確屬性
3、在字典里查找屬性

class User:def __init__(self, name):self.name = nameself.info = {"age":100}# 1、作為提示用def __getattr__(self, item):return "not found attr"# 2、找尋正確屬性# def __getattr__(self, item):# return self.name# 3、屬性不在init里,而在字典中查找# def __getattr__(self, item):# return self.info[item] if __name__ == "__main__":user = User("bobby")print(user.age) # 沒有age屬性,調用__getattr__
  • 獲取屬性時,無論屬性是否存在,都會進入__getattribute__,把持住所有屬性的入口
class User:def __init__(self, name):self.name = nameself.info = {"age":100}# 1、作為提示用def __getattr__(self, item):return "not found attr"# 獲取屬性時,無論屬性是否存在,都會進入__getattribute__,把持住所有屬性的入口# 重寫__getattribute__,注意的是重寫的方法中不能使用對象的點運算符訪問屬性,否則使用點運算符訪問屬性時,# 會再次調用__getattribute__。這樣就會陷入無限遞歸!!!!!!!# 可以使用super()方法避免這個問題。def __getattribute__(self,item):return "sad bobby"return super(User,self).__getattribute__(item) # 或使用super if __name__ == "__main__":user = User("bobby")print(user.name) # 無論有沒有age屬性,都調用__getattrbute__,并且如果有屬性,也不會輸出屬性,而是只運行__getattribute__# 因此說__getattributed__是所有獲取屬性的入口和出口

但一般不建議自己設置__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__的類,只能被讀取
class NonDataIntFiled:def __get__(self,instance,value):return self.value
  • 2、數據描述符:實現了__get__和__set__兩個魔法函數,可被讀寫
class IntField:# 實現以下三個魔法函數中的任意一個,類就能變為屬性描述符def __get__(self,instance,value):return self.valuedef __set__(self,instance,value):if isinstance(value,int):raise ValueError("int value need")if value<0:raise ValueError("positive value need")self.value = valuedef __delete__(self,instance):pass
屬性描述符的使用:

以數據描述符作為例子

  • 方法1:自定義property中的方法,替換__get__\__set__\__delete__,直接生成屬性描述符,但目前只探索到的功能有限,僅能通過該屬性描述符的對象直接調用描述符里定義的屬性,無法管理多個特定屬性
class IntField:def get(self,value):print("Get")return self.valuedef set(self, value):print("Set method")if not isinstance(value,int):raise ValueErrorself.value = valuedef delete(self):print("Delete method")del self.value# 還有個缺陷,就是屬性名會寫死為age,不能通用在其他的屬性上了age = property(get, set, delete) # 直接生成屬性描述符的對象,這樣不會寫入到__dict__里 num = IntField() num.age = 1 # 會調用set\get方法,說明是通過屬性描述符進行設置的 print(num.__dict__) # 輸出{}空字典,說明age沒有進入__dict__里# 如果是放進另外一個類空間里作為屬性,那么創建User類對象num時,num.age會寫入到__dict__中,因此不能作為屬性描述符使用了 class User():age = IntField() num = User() num.age = 1 # 這里無法調用set\get方法 print(num.__dict__) # 輸出{"age":1},說明是通過默認的__setattr__進入到__dict__中,進一步證明了,不是使用屬性描述符
  • 方法2:常規使用__get__\__set__\__delete__,生成屬性描述符后,再創建一個類,在類空間生成屬性描述符的對象,在外部創建類對象后,再進行調用
class IntField():def __get__(self,instance,value):print("__get__")return self.valuedef __set__(self, instance, value):print("__set__")if not isinstance(value,int):raise ValueErrorself.value = valueclass User():# 屬性描述符一般用在類空間或是基類age = IntField()phone = IntField() user1 = User() # age是屬性描述符的對象,作為user1的屬性使用,但沒有存入對象空間__dict__中,調用了__set__\__get__ user1.age = 100 # year是通過默認的__setattr__生成的屬性,存入了對象空間__dict__中,沒有調用__set__\__get__ user1.year = 100 print(user1.__dict__) # 只輸出了{"year":100},并沒有age哦!!!!

而最終的屬性查找順序,則是如下

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 異常,結束查找。

總結

以上是生活随笔為你收集整理的属性查找顺序的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。