__getattr__在python2.x与python3.x中的区别及其对属性截取与代理类的影响
python2.x中的新類(lèi)型類(lèi)(New-style class)與python3.x的類(lèi)一致,均繼承object類(lèi),而不繼承object的類(lèi)稱(chēng)為經(jīng)典類(lèi)(classic class),而對(duì)于這兩種類(lèi),一般實(shí)例屬性截取函數(shù)(generic instance attribute interception methods)的行為有所不同,其在3.x和2.x的新類(lèi)型類(lèi)中,不再被__x__操作符重載函數(shù)名(operator overloading name)的內(nèi)建操作調(diào)用,對(duì)于該操作符重載函數(shù)名的搜索直接在類(lèi)中搜索,而非實(shí)例中,而對(duì)于顯式名的屬性獲取,包括__x__名,仍然要路經(jīng)__getattr__,因此這是對(duì)于內(nèi)建操作行為的主要影響,這種影響進(jìn)而又影響到屬性截取以及代理類(lèi)。比如一個(gè)類(lèi)定義了__getitem__索引重載函數(shù),x是該類(lèi)的一個(gè)實(shí)例,對(duì)于經(jīng)典類(lèi)來(lái)說(shuō),x[I]與x.__getitem__(I)等價(jià),而對(duì)于新類(lèi)型類(lèi)來(lái)說(shuō),x[I]不再被__getattr__獲取,而顯式x.__getitem__仍然可以被獲取。
1.對(duì)屬性截取的影響:
首先看__getattr__在經(jīng)典類(lèi)與新類(lèi)型類(lèi)中表現(xiàn)的差異。
(1)在新類(lèi)型類(lèi)中(下列代碼在3.x中實(shí)現(xiàn)):
>>> class c:?data='spam'
?def __getattr__(self,name):
??print('getattr->'+name)
??return getattr(self.data,name)
>>> x=c()
>>> x[0]
Traceback (most recent call last):
? File "<pyshell#7>", line 1, in <module>
??? x[0]
TypeError: 'c' object does not support indexing
構(gòu)造了一個(gè)名為c的類(lèi),類(lèi)長(zhǎng)__getattr__方法可截取實(shí)例屬性,然后打印截取到的屬性名,最后返回實(shí)例對(duì)象的data對(duì)象的name方法結(jié)果。而對(duì)于x[0]內(nèi)建操作表達(dá)式,則拋出了異常,該異常為c對(duì)象不支持索引,因此可以看出x[0]是直接在類(lèi)中進(jìn)行搜索,而跳過(guò)了實(shí)例屬性截取函數(shù)__getattr__。
>>> getattr->__getitem__ x.__getitem__(0) getattr->__getitem__ 's'而x.__getitem__(0)方法可以被__getattr__獲取,類(lèi)似的,對(duì)于其他內(nèi)建操作,比如,x+'eggs',與x.__add__('eggs'),也有相同的反應(yīng)。
>>> getattr->__add__ x.__add__('eggs') getattr->__add__ 'spameggs' >>> x+'eggs' Traceback (most recent call last):File "<pyshell#12>", line 1, in <module>x+'eggs' TypeError: unsupported operand type(s) for +: 'c' and 'str' >>> type(x).__getitem__(x,0)Traceback (most recent call last):
? File "<pyshell#18>", line 1, in <module>
??? type(x).__getitem__(x,0)
AttributeError: type object 'c' has no attribute '__getitem__'
當(dāng)用x的類(lèi)(即c)調(diào)用__getitem__,可以預(yù)想到的,拋出AttributeError,因?yàn)閏并沒(méi)有__getitem__方法。
(2)以上代碼在經(jīng)典類(lèi)中(在2.x中實(shí)現(xiàn)):
>>> class c:data='spam'def __getattr__(self,name):print('getattr->'+name)return getattr(self.data,name)File "<pyshell#0>", line 2class c:^ IndentationError: unexpected indent >>> class c:data='spam'def __getattr__(self,name):print('getattr->'+name)return getattr(self.data,name)>>> x=c() >>> x[0] getattr->__getitem__ 's' >>> getattr->__getitem__ x.__getitem__(0) getattr->__getitem__ 's' >>> getattr->__add__ x.__add__('eggs') getattr->__add__ 'spameggs' >>> x+'eggs' getattr->__coerce__ getattr->__add__ 'spameggs'可以看到,在經(jīng)典類(lèi)型中,測(cè)試全部通過(guò)。
>>> type(x).__getitem__(0)Traceback (most recent call last):File "<pyshell#8>", line 1, in <module>type(x).__getitem__(0) TypeError: descriptor '__getitem__' requires a 'instance' object but received a 'int'但是,嘗試用c類(lèi)調(diào)用__getitem__,卻拋出異常,主要是描述符(descriptor)的參數(shù)錯(cuò)誤造成的,關(guān)于描述符的總結(jié),將在后面的文章中專(zhuān)門(mén)整理。
2.對(duì)代理類(lèi)的影響
實(shí)際上,在屬性截取中,已經(jīng)提到,在新類(lèi)型類(lèi)中,當(dāng)直接用隱式的內(nèi)建操作表達(dá)式,如x[i],x+等,拋出AttributError的異常,因?yàn)檫@種情況下,是直接從類(lèi)開(kāi)始搜索的,而c類(lèi)中沒(méi)有,所以才拋出了異常,那該怎么辦呢?一個(gè)很自然的辦法就是在類(lèi)中,對(duì)要代理的隱式內(nèi)建操作表達(dá)式進(jìn)行重新定義,所以類(lèi)就具備了要代理操作屬性。
>>> class c:data='spam'def __getattr__(self,name):print('getattr->'+name)return getattr(self.data,name)def __getitem__(self,i):print('getitem:'+str(i))return self.data[i]def __add__(self,other):print('add->'+other)return getattr(self.data,'__add__')(other)上述代碼在3.x中實(shí)現(xiàn),通過(guò)對(duì)類(lèi)c重新定義__getitem__,__add__重新定義實(shí)現(xiàn)了代理索引和加操作。
>>> x=c() >>> x.upper() getattr->upper 'SPAM'可以看到__getattr__截取了一般方法upper()。
>>> x[0] getitem:0 's' >>> x.__getitem__(0) getitem:0 's' >>> x+'eggs' add->eggs 'spameggs' >>> x.__add__('eggs') add->eggs 'spameggs'可以看到,代理成功。
(3)進(jìn)一步的理解
事實(shí)上,子類(lèi)繼承基類(lèi)(超類(lèi))的屬性或者方法若在子類(lèi)中沒(méi)有重載,而子類(lèi)實(shí)例若調(diào)用該屬性,將不被__getattr__攔截,直接調(diào)用基類(lèi)的屬性。如下代碼:
>>> class c:def test(self):print('test from c')>>> class d(c):def __getattr__(self,attr):print('getattr'+attr)>>> x=d() >>> x.test() test from c?
轉(zhuǎn)載于:https://www.cnblogs.com/johnyang/p/10461887.html
總結(jié)
以上是生活随笔為你收集整理的__getattr__在python2.x与python3.x中的区别及其对属性截取与代理类的影响的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。