Python中幾種屬性訪問的區(qū)別與用法詳解
起步
在Python中,對(duì)于一個(gè)對(duì)象的屬性訪問,我們一般采用的是點(diǎn)(.)屬性運(yùn)算符進(jìn)行操作。例如,有一個(gè)類實(shí)例對(duì)象foo,它有一個(gè)name屬性,那便可以使用foo.name對(duì)此屬性進(jìn)行訪問。一般而言,點(diǎn)(.)屬性運(yùn)算符比較直觀,也是我們經(jīng)常碰到的一種屬性訪問方式。
python的提供一系列和屬性訪問有關(guān)的特殊方法: __get__ , __getattr__ , __getattribute__ , __getitem__ 。本文闡述它們的區(qū)別和用法。
屬性的訪問機(jī)制
一般情況下,屬性訪問的默認(rèn)行為是從對(duì)象的字典中獲取,并當(dāng)獲取不到時(shí)會(huì)沿著一定的查找鏈進(jìn)行查找。例如 a.x 的查找鏈就是,從 a.__dict__['x'] ,然后是 type(a).__dict__['x'] ,再通過 type(a) 的基類開始查找。
若查找鏈都獲取不到屬性,則拋出 AttributeError 異常。
__getattr__ 方法
__getattr__函數(shù)的作用: 如果屬性查找(attribute lookup)在實(shí)例以及對(duì)應(yīng)的類中(通過__dict__)失敗, 那么會(huì)調(diào)用到類的__getattr__函數(shù), 如果沒有定義這個(gè)函數(shù),那么拋出AttributeError異常。由此可見,__getattr__一定是作用于屬性查找的最后一步,兜底。
這個(gè)方法是當(dāng)對(duì)象的屬性不存在是調(diào)用。如果通過正常的機(jī)制能找到對(duì)象屬性的話,不會(huì)調(diào)用 __getattr__ 方法。
class A: a = 1 def __getattr__(self, item): print('__getattr__ call') return item t = A() print(t.a) print(t.b) # output 1 __getattr__ call b
__getattribute__ 方法
這個(gè)方法會(huì)被無條件調(diào)用。不管屬性存不存在。如果類中還定義了 __getattr__ ,則不會(huì)調(diào)用 __getattr__() 方法,除非在 __getattribute__ 方法中顯示調(diào)用 __getattr__() 或者拋出了 AttributeError 。
class A: a = 1 def __getattribute__(self, item): print('__getattribute__ call') raise AttributeError def __getattr__(self, item): print('__getattr__ call') return item t = A() print(t.a) print(t.b)
所以一般情況下,為了保留 __getattr__ 的作用, __getattribute__() 方法中一般返回父類的同名方法:
def __getattribute__(self, item): return object.__getattribute__(self, item)
使用基類的方法來獲取屬性能避免在方法中出現(xiàn)無限遞歸的情況。
__get__ 方法
這個(gè)方法比較簡(jiǎn)單說明,它與前面的關(guān)系不大。
如果一個(gè)類中定義了 __get__() , __set__() 或 __delete__() 中的任何方法。則這個(gè)類的對(duì)象稱為描述符。
class Descri(object): def __get__(self, obj, type=None): print("call get") def __set__(self, obj, value): print("call set") class A(object): x = Descri() a = A() a.__dict__['x'] = 1 # 不會(huì)調(diào)用 __get__ a.x # 調(diào)用 __get__
如果查找的屬性是在描述符對(duì)象中,則這個(gè)描述符會(huì)覆蓋上文說的屬性訪問機(jī)制,體現(xiàn)在查找鏈的不同,而這個(gè)行文也會(huì)因?yàn)檎{(diào)用的不同而稍有不一樣:
- 如果調(diào)用是對(duì)象實(shí)例(題目中的調(diào)用方式), a.x 則轉(zhuǎn)換為調(diào)用: 。 type(a).__dict__['x'].__get__(a, type(a))
- 如果調(diào)用的是類屬性, A.x 則轉(zhuǎn)換為: A.__dict__['x'].__get__(None, A)
- 其他情況見文末參考資料的文檔
__getitem__ 方法
這個(gè)調(diào)用也屬于無條件調(diào)用,這點(diǎn)與 __getattribute__ 一致。區(qū)別在于 __getitem__ 讓類實(shí)例允許 [] 運(yùn)算,可以這樣理解:
- __getattribute__ 適用于所有 . 運(yùn)算符;
- __getitem__ 適用于所有 [] 運(yùn)算符。
class A(object): a = 1 def __getitem__(self, item): print('__getitem__ call') return item t = A() print(t['a']) print(t['b'])
如果僅僅想要對(duì)象能夠通過 [] 獲取對(duì)象屬性可以簡(jiǎn)單的:
def __getitem(self, item): return object.__getattribute__(self, item)
總結(jié)
當(dāng)這幾個(gè)方法同時(shí)出現(xiàn)可能就會(huì)擾亂你了。我在網(wǎng)上看到一份示例還不錯(cuò),稍微改了下:
class C(object): a = 'abc' def __getattribute__(self, *args, **kwargs): print("__getattribute__() is called") return object.__getattribute__(self, *args, **kwargs) # return "haha" def __getattr__(self, name): print("__getattr__() is called ") return name + " from getattr" def __get__(self, instance, owner): print("__get__() is called", instance, owner) return self def __getitem__(self, item): print('__getitem__ call') return object.__getattribute__(self, item) def foo(self, x): print(x) class C2(object): d = C() if __name__ == '__main__': c = C() c2 = C2() print(c.a) print(c.zzzzzzzz) c2.d print(c2.d.a) print(c['a'])
可以結(jié)合輸出慢慢理解,這里還沒涉及繼承關(guān)系呢??傊?,每個(gè)以 __ get 為前綴的方法都是獲取對(duì)象內(nèi)部數(shù)據(jù)的鉤子,名稱不一樣,用途也存在較大的差異,只有在實(shí)踐中理解它們,才能真正掌握它們的用法。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
詳解Python中數(shù)據(jù)庫管理模塊shelve和dbm的應(yīng)用
作為常用的 python 自帶數(shù)據(jù)庫管理模塊,shelve 和 dbm 都是非常方便的對(duì)象持久化存儲(chǔ)和檢索工具,本文將從用法、優(yōu)勢(shì)以及不同點(diǎn)等方面進(jìn)行介紹,希望對(duì)大家有所幫助2023-10-10人工智能學(xué)習(xí)Pytorch梯度下降優(yōu)化示例詳解
這篇文章主要為大家介紹了人工智能學(xué)習(xí)Pytorch梯度下降優(yōu)化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2021-11-11解決python3捕獲cx_oracle拋出的異常錯(cuò)誤問題
今天小編就為大家分享一篇解決python3捕獲cx_oracle拋出的異常錯(cuò)誤問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-10-10Python實(shí)現(xiàn)朗讀在線音頻和本地音頻
在日常的Python軟件開發(fā)中,我們經(jīng)常會(huì)遇到一個(gè)非常重要的功能需求——讓程序能夠讀取并顯示文本內(nèi)容,下面我們就來學(xué)習(xí)一下Python實(shí)現(xiàn)朗讀音頻的具體操作吧2024-03-03OpenCV+python3實(shí)現(xiàn)視頻分解成圖片
這篇文章主要為大家詳細(xì)介紹了OpenCV+python3實(shí)現(xiàn)視頻分解成圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Selenium+BeautifulSoup+json獲取Script標(biāo)簽內(nèi)的json數(shù)據(jù)
這篇文章主要介紹了Selenium+BeautifulSoup+json獲取Script標(biāo)簽內(nèi)的json數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12pytorch 中pad函數(shù)toch.nn.functional.pad()的用法
今天小編就為大家分享一篇pytorch 中pad函數(shù)toch.nn.functional.pad()的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-01-01跟老齊學(xué)Python之通過Python連接數(shù)據(jù)庫
現(xiàn)在在做python的時(shí)候需要用到數(shù)據(jù)庫,于是自己重新整理了一下數(shù)據(jù)庫的知識(shí),并且熟悉了python中MysqlDB模塊的功能和函數(shù)等接口,現(xiàn)在系統(tǒng)地來總結(jié)一下吧2014-10-10