python魔法方法-屬性訪問(wèn)控制詳解
屬性訪問(wèn)控制
所謂的屬性訪問(wèn)控制就是控制點(diǎn)號(hào)訪問(wèn)屬性的行為,而且不僅是類(lèi)的外部,連類(lèi)的內(nèi)部也受控制,代碼見(jiàn)真章,邊看代碼邊解釋?zhuān)?/p>
•__getattr__(self, item)
定義當(dāng)訪問(wèn)不存在的屬性時(shí)的行為,注意是不存在的屬性。
class Foo(object): def __init__(self, value): self.value = value def __getattr__(self, item): print item # 查看得到的參數(shù)是什么 print type(item) # 參數(shù)的類(lèi)型是什么 return 'attr:%s' % item # 最后返回一個(gè)東西看其行為如何 a = Foo('scolia') # 創(chuàng)建一個(gè)實(shí)例
測(cè)試:
print a.value print type(a.value)
其行為和沒(méi)定義前正常,下面看看訪問(wèn)一個(gè)不存在的屬性時(shí)會(huì)發(fā)生什么:
print a.abc
按照平常的情況,訪問(wèn)不存在的屬性時(shí)肯定會(huì)拋出異常,但是這里輸出了三行,前兩個(gè)是方法輸出的,最后一行是外部的print語(yǔ)句輸出的,輸出的是方法的return值。方法得到的是我們?cè)L問(wèn)的屬性名,而且是以字符串的形式。
知道了以上信息后,我們就可以定制更多:
class Foo(object): def __init__(self, value): self.value = value def __getattr__(self, item): if item == 'scolia': return 'can not set attr: %s' % item # 訪問(wèn)不存在的scolia屬性時(shí),打印一句話而不報(bào)錯(cuò) else: raise AttributeError('not attr name: %s' % item) # 訪問(wèn)其他不存在的屬性時(shí),觸發(fā)異常。
測(cè)試:
a = Foo(123) print a.value # 訪問(wèn)存在的屬性
print a.scolia # 訪問(wèn)不存在的屬性,但我們做了特殊處理的
沒(méi)有觸發(fā)異常,和我們?cè)O(shè)想的一樣。
print a.good # 訪問(wèn)不存在的屬性,但應(yīng)該觸發(fā)異常的
觸發(fā)了我們想要的異常。
這里要再?gòu)?qiáng)調(diào)一遍,必須是訪問(wèn)不存在的屬性時(shí),才會(huì)調(diào)用這個(gè)方法,例如:
a.scolia = 321 print a.scolia
因?yàn)檫@個(gè)屬性已經(jīng)存在了(我們手動(dòng)添加了),所以訪問(wèn)它的時(shí)候并沒(méi)有調(diào)用這個(gè)方法,而在方法里所做的任何處理,也不會(huì)有效。
更高級(jí)的技巧:
class Foo(object): def __init__(self, value, defulat=None): self.value = value self.__defulat = defulat def __getattr__(self, item): item = item.lower() # 用字符串的方法對(duì)其進(jìn)行小寫(xiě) if item in self.__dict__: return self.__dict__[item] # 返回相應(yīng)的屬性 else: self.__dict__[item] = self.__defulat # 若屬性不存在則添加這個(gè)屬性并使用默認(rèn)值 return self.__dict__[item] a = Foo(123) a.scolia = 321 print a.SCOlia print a.good
我們實(shí)現(xiàn)了屬性的不區(qū)分大小寫(xiě)訪問(wèn)和自動(dòng)添加不存在的屬性。
這里的秘訣在于活用 __dict__ 這個(gè)屬性,我在類(lèi)的屬性中已經(jīng)討論過(guò)這個(gè)屬性。這個(gè)屬性由python自動(dòng)創(chuàng)建,是一個(gè)字典,包含對(duì)象的所有屬性,字典里的鍵就是屬性名,對(duì)應(yīng)的值就是屬性值。所以這里在這個(gè)字典中添加了鍵和值,就相當(dāng)于為對(duì)象添加了屬性和屬性值。
•__setattr__(self, key, value)
定義了設(shè)置屬性時(shí)的行為,包括在 __init__ 初始化函數(shù)中的設(shè)置行為:
class Foo(object): def __init__(self, value, defulat=None): self.value = value def __setattr__(self, key, value): print key, type(key) print value, type(value) a = Foo('scolia') b = Foo(123)
這里可以看到初始化函數(shù)中的屬性添加的行為也受到了控制,其中 key 得到的是屬性名,以字符串的形式;而 value 得到的是屬性值,屬性值根據(jù)輸入的不同而不同。
在這里,我們僅僅只是打印了幾句話,而沒(méi)有進(jìn)行屬性的添加,所以當(dāng)我們?cè)噲D訪問(wèn)相應(yīng)的屬性時(shí),會(huì)發(fā)現(xiàn)根本就沒(méi)有:
print a.value
觸發(fā)了異常,表示沒(méi)有相應(yīng)的屬性。
知道了這些之后我們可以做很多事情,例如將所有的屬性名變成小寫(xiě)或大寫(xiě),控制某些屬性名不能添加之類(lèi)的,就不再舉例。不過(guò),這里你總不可能用 self.key = value 來(lái)添加屬性吧,因?yàn)?key 始終是一個(gè)字符串。這個(gè)時(shí)候就要使用 __dict__ 屬性了,向這個(gè)字典中添加相應(yīng)的鍵值對(duì)就可以了,具體就不再演示了。
•__delattr__(self, item)
定義了刪除一個(gè)屬性時(shí)的行為,item 得到的也是一個(gè)字符串形式的屬性名,具體細(xì)節(jié)也無(wú)序多說(shuō),只要 del 掉 __dict__ 字典中對(duì)應(yīng)是鍵和值就行了。另外,刪除不存在的屬性時(shí)調(diào)用的也是這個(gè)方法。
•__getattribute__(self, name)
這個(gè)方法定義了所有屬性訪問(wèn)的行為,注意是所有,而不是 __getattr__ 中的不存在。當(dāng)實(shí)現(xiàn)了這個(gè)方法之后,將會(huì)覆蓋 __getattr__ 方法,畢竟所有涵蓋了不存在。
這個(gè)方法只在新式類(lèi)中有效。
但是,你也可以顯式的調(diào)用 __getattr__,例如 a.__getattr__ 來(lái)使用這個(gè)被掩蓋的方法,或者是觸發(fā) AttributeError 異常時(shí)也會(huì)自動(dòng)調(diào)用它。
然而,非常不建議使用這個(gè)方法,因?yàn)榭赡軙?huì)很多不可預(yù)知的異常情況,最常見(jiàn)的就是無(wú)盡的遞歸調(diào)用,例如:
class Foo(object): def __init__(self, value): self.value = value def __getattribute__(self, item): return self.__dict__[item] a = Foo('scolia') print a.value
這段代碼看起來(lái)很正常,但是這里有一個(gè)陷阱,因?yàn)轭?lèi)中的所有的屬性訪問(wèn)都是受這幾個(gè)魔法方法控制的,包括上面介紹的幾個(gè)魔法方法。它們似乎比普通的魔法方法擁有更高的權(quán)限一般。
但這就導(dǎo)致了一個(gè)問(wèn)題,例如這里的 self.__dict__[item] ,這句話也受屬性訪問(wèn)的控制,盡管這個(gè)屬性是 python 為我們創(chuàng)建的。
也就是說(shuō)獲取 self.__dict__ 時(shí),會(huì)再次調(diào)用 __getattribute__ 方法,然后方法內(nèi)又調(diào)用了 self.__dict__ 。這樣無(wú)限循環(huán)下去,最終會(huì)拋出一個(gè)異常。
異常信息非常長(zhǎng),這里我是拉到最后才截的圖。
其實(shí)不僅這個(gè)魔法方法會(huì)導(dǎo)致這樣異常,上面討論的幾種魔法方法可能都會(huì)出現(xiàn)這個(gè)問(wèn)題,只不過(guò)這個(gè)魔法方法的權(quán)限更大,所以異常出現(xiàn)的可能性更高一些。
這也就是不推薦這個(gè)魔法方法的原因,而使用其他的屬性控制方法的時(shí)候也要小心。
而到目前為止,我們所學(xué)到的屬性訪問(wèn)的方法只有兩種,一是直接用點(diǎn)號(hào)訪問(wèn),還有就是先通過(guò)點(diǎn)號(hào)訪問(wèn)__dict__ 屬性,然后在這個(gè)字典中獲取相應(yīng)的鍵值對(duì)。而這兩種方法都受到了 __getattribute__ 的控制,調(diào)用它們就相當(dāng)于沒(méi)有終點(diǎn)的自調(diào)用(有終點(diǎn)的自調(diào)用有時(shí)能提升效率),那么這個(gè)方法到底要怎么用呢?
技巧就是調(diào)用父類(lèi)的這個(gè)方法:
class Foo(object): def __init__(self, value): self.value = value def __getattribute__(self, item): return object.__getattribute__(self, item) # 非綁定方法要顯式傳遞self a = Foo('scolia') print a.value
這里調(diào)用的是object的這個(gè)方法,如果是涉及到繼承的話:
class Boo(Foo): def __init__(self, value): self.value = value def __getattribute__(self, item): return Foo.__getattribute__(self, item) # return super(Boo, self).__getattribute__(item) 也可以使用super函數(shù)讓python自動(dòng)在其父類(lèi)們尋找這個(gè)方法。 a = Foo('scolia') print a.value b = Boo(123) print b.value
訪問(wèn)正常。
其實(shí)最后調(diào)用了還是 object 或其他內(nèi)置類(lèi)型的方法。
而我們姑且不起探究object到底是怎么實(shí)現(xiàn)的,因?yàn)檫@可能是用 C 所寫(xiě)的,只要會(huì)用就可以,雖然這個(gè)方法用的也不多。
最后附上一個(gè)完整的例子:
class Foo(object): def __init__(self, value): self.value = value def __getattr__(self, item): if item == 'scolia': return 'no attr:%s' % item elif item in self.__dict__: return self.__dict__[item] else: raise AttributeError('no attr:%s' % item) def __setattr__(self, key, value): if key == 'good': print 'can not set the attr: good' else: self.__dict__[key] = value def __delattr__(self, item): if item == 'a': print 'no attr: good' else: del self.__dict__[item] def __getattribute__(self, item): if item == 'a': raise AttributeError('not a') return object.__getattribute__(self, item) a = Foo('scolia') print a.value # 正常訪問(wèn) a.a = 123 # __getattribute__會(huì)觸發(fā)AttributeError異常,此時(shí)調(diào)用__getattr__ # 而__getattr__添加了這個(gè)屬性,所以最后異常沒(méi)有觸發(fā),屬性也添加了 print a.a # 結(jié)果能夠訪問(wèn) del a.a # 試圖刪除這個(gè)屬性 print a.a # 刪除行為被阻止了,所以該屬性還在 a.good = 'good' # 因?yàn)樘砑颖蛔柚沽? print a.good # 所以訪問(wèn)失敗了
結(jié)果:
以上這篇python魔法方法-屬性訪問(wèn)控制詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Django 后臺(tái)獲取文件列表 InMemoryUploadedFile的例子
今天小編就為大家分享一篇Django 后臺(tái)獲取文件列表 InMemoryUploadedFile的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08Python 循環(huán)語(yǔ)句之 while,for語(yǔ)句詳解
Python中有兩種循環(huán),分別為:for循環(huán)和while循環(huán)。 for循環(huán)可以遍歷任何序列的項(xiàng)目,如一個(gè)列表或者一個(gè)字符串。while 語(yǔ)句用于循環(huán)執(zhí)行程序,即在某條件下,循環(huán)執(zhí)行某段程序,以處理需要重復(fù)處理的相同任務(wù)。2018-04-04scrapy-redis源碼分析之發(fā)送POST請(qǐng)求詳解
這篇文章主要給大家介紹了關(guān)于scrapy-redis源碼分析之發(fā)送POST請(qǐng)求的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用scrapy-redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05解決Pycharm 中遇到Unresolved reference ''sklearn''的問(wèn)題
這篇文章主要介紹了解決Pycharm 中遇到Unresolved reference 'sklearn'的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07python實(shí)現(xiàn)可下載音樂(lè)的音樂(lè)播放器
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)可下載音樂(lè)的音樂(lè)播放器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02python爬蟲(chóng)流程基礎(chǔ)示例零基礎(chǔ)學(xué)習(xí)
這篇文章主要為大家介紹了python爬蟲(chóng)流程基礎(chǔ)示例零基礎(chǔ)學(xué)習(xí),我們將討論 Python 網(wǎng)絡(luò)編程中的爬蟲(chóng)基礎(chǔ),作為一個(gè)完全的初學(xué)者,你將學(xué)習(xí)到爬蟲(chóng)的基本概念、常用庫(kù)以及如何編寫(xiě)一個(gè)簡(jiǎn)單的爬蟲(chóng)2023-06-06對(duì)pandas將dataframe中某列按照條件賦值的實(shí)例講解
今天小編就為大家分享一篇對(duì)pandas將dataframe中某列按照條件賦值的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-11-11Python樹(shù)的序列化與反序列化的實(shí)現(xiàn)
在本文中,我們將深入討論如何實(shí)現(xiàn)樹(shù)的序列化與反序列化算法,提供Python代碼實(shí)現(xiàn),并詳細(xì)說(shuō)明算法的原理和步驟,感興趣的可以了解一下2023-11-11Python?pytest自動(dòng)化測(cè)試庫(kù)十個(gè)強(qiáng)大用法示例
本文將介紹Python的pytest庫(kù)的10個(gè)強(qiáng)大用法,并提供相應(yīng)的代碼示例,幫助你更好地理解和應(yīng)用單元測(cè)試,它提供了許多高級(jí)功能和便利的用法,能夠讓我們更輕松地編寫(xiě)和執(zhí)行單元測(cè)試2024-01-01Python3+django2.0+apache2+ubuntu14部署網(wǎng)站上線的方法
這篇文章主要介紹了Python3+django2.0+apache2+ubuntu14部署網(wǎng)站上線的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07