__getattr__在python2.x与python3.x中的区别及其对属性截取与代理类的影响
python2.x中的新類型類(New-style class)與python3.x的類一致,均繼承object類,而不繼承object的類稱為經(jīng)典類(classic class),而對于這兩種類,一般實例屬性截取函數(shù)(generic instance attribute interception methods)的行為有所不同,其在3.x和2.x的新類型類中,不再被__x__操作符重載函數(shù)名(operator overloading name)的內(nèi)建操作調(diào)用,對于該操作符重載函數(shù)名的搜索直接在類中搜索,而非實例中,而對于顯式名的屬性獲取,包括__x__名,仍然要路經(jīng)__getattr__,因此這是對于內(nèi)建操作行為的主要影響,這種影響進(jìn)而又影響到屬性截取以及代理類。比如一個類定義了__getitem__索引重載函數(shù),x是該類的一個實例,對于經(jīng)典類來說,x[I]與x.__getitem__(I)等價,而對于新類型類來說,x[I]不再被__getattr__獲取,而顯式x.__getitem__仍然可以被獲取。
1.對屬性截取的影響:
首先看__getattr__在經(jīng)典類與新類型類中表現(xiàn)的差異。
(1)在新類型類中(下列代碼在3.x中實現(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)造了一個名為c的類,類長__getattr__方法可截取實例屬性,然后打印截取到的屬性名,最后返回實例對象的data對象的name方法結(jié)果。而對于x[0]內(nèi)建操作表達(dá)式,則拋出了異常,該異常為c對象不支持索引,因此可以看出x[0]是直接在類中進(jìn)行搜索,而跳過了實例屬性截取函數(shù)__getattr__。
>>> getattr->__getitem__ x.__getitem__(0) getattr->__getitem__ 's'而x.__getitem__(0)方法可以被__getattr__獲取,類似的,對于其他內(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的類(即c)調(diào)用__getitem__,可以預(yù)想到的,拋出AttributeError,因為c并沒有__getitem__方法。
(2)以上代碼在經(jīng)典類中(在2.x中實現(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)典類型中,測試全部通過。
>>> 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類調(diào)用__getitem__,卻拋出異常,主要是描述符(descriptor)的參數(shù)錯誤造成的,關(guān)于描述符的總結(jié),將在后面的文章中專門整理。
2.對代理類的影響
實際上,在屬性截取中,已經(jīng)提到,在新類型類中,當(dāng)直接用隱式的內(nèi)建操作表達(dá)式,如x[i],x+等,拋出AttributError的異常,因為這種情況下,是直接從類開始搜索的,而c類中沒有,所以才拋出了異常,那該怎么辦呢?一個很自然的辦法就是在類中,對要代理的隱式內(nèi)建操作表達(dá)式進(jìn)行重新定義,所以類就具備了要代理操作屬性。
>>> 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中實現(xiàn),通過對類c重新定義__getitem__,__add__重新定義實現(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)一步的理解
事實上,子類繼承基類(超類)的屬性或者方法若在子類中沒有重載,而子類實例若調(diào)用該屬性,將不被__getattr__攔截,直接調(diào)用基類的屬性。如下代碼:
>>> 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中的区别及其对属性截取与代理类的影响的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【小程序】项目结构
- 下一篇: Python调用ansible API系