python getattr和getattribute_python中__getattr__和__getattribute__区别
重載__getattr__方法對類及其實例未定義的屬性有效。如果訪問的屬性存在,就不會調(diào)用__getattr__方法。這個屬性的存在,包括類屬性和實例屬性
classClassA:
x= 'a'
def __init__(self):
self.y= 'b'
def __getattr__(self, item):return '__getattr__'
if __name__ == '__main__':
a=ClassA()print(a.x)#輸出結(jié)果 a
#使用實例直接訪問實例存在的屬性時,不會調(diào)用__getattr__方法
print(a.y) #輸出結(jié)果 b
#使用實例直接訪問實例不存在的屬性時,會調(diào)用__getattr__方法
print(a.z) #輸出結(jié)果 __getattr__
__getattribute__僅在新式類中可用,重載__getattrbute__方法對類實例的每個屬性訪問都有效,無論屬性存不存在都會先調(diào)用__getattribute__方法
classClassA:
x= 'a'
def __init__(self):
self.y= 'b'
def __getattribute__(self, item):return '__getattribute__'
if __name__ == '__main__':
a=ClassA()#使用實例直接訪問存在的類屬性時,會調(diào)用__getattribute__方法
print(a.x) #輸出結(jié)果 __getattribute__
#使用實例直接訪問實例存在的實例屬性時,會調(diào)用__getattribute__方法
print(a.y) #輸出結(jié)果 __getattribute__
#使用實例直接訪問實例不存在的實例屬性時,也會調(diào)用__getattribute__方法
print(a.z) #輸出結(jié)果 __getattribute__
當(dāng)同時定義__getattribute__和__getattr__時,__getattr__方法不會再被調(diào)用,除非顯示調(diào)用__getattr__方法或引發(fā)AttributeError異常。
classClassA:def __getattr__(self, item):print('__getattr__')def __getattribute__(self, item):print('__getatttribute__')if __name__ == '__main__':
a=ClassA()
a.x
運行結(jié)果__getatttribute__
由于__getattr__只針對未定義屬性的調(diào)用,所以它可以在自己的代碼中自由地獲取其他屬性,
而__getattribute__針對所有的屬性運行,因此要十分注意避免在訪問其他屬性時,再次調(diào)用自身的遞歸循環(huán)。死循環(huán)!!
當(dāng)在__getattribute__代碼塊中,再次執(zhí)行屬性的獲取操作時,會再次觸發(fā)__getattribute__方法的調(diào)用,代碼將會陷入無限遞歸,直到Python遞歸深度限制(重載__setter__? __setattr__方法也會有這個問題)。
示例代碼(無限遞歸):
classClassA:
x= 'a'
def __getattribute__(self, item):print('__getattribute__')return self.item #再次出現(xiàn)屬性的獲取操作,會再次觸發(fā)__getattribute__的調(diào)用
#相當(dāng)于return self.__getattribute__(item)
if __name__ == '__main__':
a=ClassA()
a.x
運行結(jié)果,達(dá)到最大遞歸深度
ecursionError: maximum recursion depth exceeded
也沒辦法通過從__dict__取值的方式來避免無限遞歸(重寫__setattr__可以通過__dict__取值的方式來避免無限遞歸)
classClassA:
x= 'a'
def __getattribute__(self, name):return self.__dict__[name] #__dict__魔法方法可以查看對象的屬性,返回一個字典,鍵代表屬性名 ,這樣再次出現(xiàn)屬性獲取的操作,會再次觸發(fā)__getattribute__if __name__ == '__main__':
a=ClassA()
a.x#無限遞歸
為了避免無限遞歸,應(yīng)該把獲取屬性的方法?__getattribute__指向一個更高的超類,例如object(因為__getattribute__只在新式類中可用,而新式類所有的類都顯式或隱式地繼承自object,所以對于新式類來說,object是所有新式類的超類)。利用super()方法
classClassA:
x= 'a' #類屬性
def __getattribute__(self, item):print('__getattribute__')return super().__getattribute__(self, item)if __name__ == '__main__':
a=ClassA()print(a.x) #輸出__getattribute__
a
調(diào)用__getattr__詳細(xì)過程如下:
obj.attribute
首先會在對象的實例屬性中尋找,找不到執(zhí)行第二步
來到對象所在的類中查找類屬性,如果還找不到執(zhí)行第三步
來到對象的繼承鏈上尋找,如果還找不到執(zhí)行第四步
調(diào)用obj.__getattr__方法,如果用戶沒有定義或者還是找不到,拋出AttributeError異常,屬性查找失敗
classMyClass:def __init__(self, x):
self.x=x>>> obj = MyClass(1)>>>obj.y
AttributeError:'MyClass' object has no attribute 'a'
如上代碼,沒有定義__getattr__魔法方法,又找不到屬性,就會拋出異常
調(diào)用__getattrIbute__方法
當(dāng)我們調(diào)用對象的屬性時,首先會調(diào)用__getattribute__魔法方法。無論對象存不存在;當(dāng)__getattribute__查找失敗,就會去調(diào)用__getattr__方法。
obj.x
obj.__getattribute__(x)
這兩個代碼其實是等價的
使用__getattribute__魔法方法時,要返回父類的方法,(super函數(shù))不然很難寫對!!會導(dǎo)致無限遞歸!
另外,內(nèi)置的bif? ?getattr和hasattr也會觸發(fā)這個魔法方法__getattribute__!!
其他細(xì)節(jié)需要注意
1. _getattribute__的查找順序
classMyClass:
x= 999 #類屬性x
def __init__(self, x): #形參x
self.x=x #實例屬性xdef __getattribute__(self, item):print('正在獲取屬性{}'.format(item))return super(MyClass, self).__getattribute__(item)
>>> obj = MyClass(2)
>>> print(obj.x)
正在獲取屬性x
2
>>> del obj.x #刪除了實例屬性x
>>> print(obj.x) #此時訪問的是類屬性
正在獲取屬性
999
上面代碼中,定義了一個類屬性x和一個實例屬性x,這兩個屬性同名,根據(jù)Python語法規(guī)則,當(dāng)對象獲取屬性x的時候,首先會在實例屬性中尋找,如果找不到才回去類屬性中查找
這便是__getattribute__的查找順序。通常該方法在框架中可能會用到,一般情況下無需使用
2. super 對象沒有 __getattr__魔法方法!!
>>> classC:def __getattr__(self, name):print(1)return super().__getattr__(name)def __getattribute__(self, name):print(2)return super().__getattribute__(name)def __setattr__(self, name, value):print(3)
super().__setattr__(name, value)def __delattr__(self, name):print(4)
super().__delattr__(name)>>> c =C()>>>c.x
運行結(jié)果:>>> c =C()>>>c.x2
1Traceback (most recent call last):
File"", line 1, in c.x
File"", line 4, in __getattr__
return super().__getattr__(name)
AttributeError:'super' object has no attribute '__getattr__'
分析一下:首先 c.x 會先調(diào)用 __getattribute__() 魔法方法,打印 2;然后調(diào)用 super().__getattribute__(),找不到屬性名 x,因此會緊接著調(diào)用 __getattr__() ,于是打印 1;你希望最后以 super().__getattr__() 終了的時候,Python 竟然告訴你 AttributeError,super 對象木有 __getattr__ !
證明:
>>>dir(super)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__self__', '__self_class__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__thisclass__']
(dir()?函數(shù)不帶參數(shù)時,返回當(dāng)前范圍內(nèi)的變量、方法和定義的類型列表;帶參數(shù)時,返回參數(shù)的屬性、方法列表。如果參數(shù)包含方法__dir__(),該方法將被調(diào)用。如果參數(shù)不包含__dir__(),該方法將最大限度地收集參數(shù)信息)
3. 初學(xué)常犯錯誤沒有“觀前顧后”!
例如:編寫一個 Counter 類,用于實時檢測對象有多少個屬性。
classCounter:def __init__(self):
self.counter=0 # 屬性賦值,這里會觸發(fā) __setattr__ 調(diào)用def __setattr__(self, name, value): #self是綁定的對象,name是屬性名(name必須是字符串),value是為對象屬性賦的值
self.counter+= 1super().__setattr__(name, value) #這時候 self.counter 還沒有定義,所以沒法 += 1,錯誤的根源。def __delattr__(self, name):
self.counter-= 1super().__delattr__(name)
>>>c =Counter()
運行結(jié)果:
AttributeError: 'Counter' object has no attribute 'counter'
正確代碼:
classCounter:def __init__(self):
super().__setattr__('counter', 0) #調(diào)用基類的賦值魔法方法__setattr__(name,value) name必須是字符串!def __setattr__(self, name, value):
super().__setattr__('counter', self.counter + 1)
super().__setattr__(name, value)def __delattr__(self, name):
super().__setattr__('counter', self.counter - 1)
super().__delattr__(name)
另外的
__setattr__(self, name, value)
定義當(dāng)一個屬性被設(shè)置時的行為
__delattr__(self, name)
定義當(dāng)一個屬性被刪除時的行為
總結(jié)
以上是生活随笔為你收集整理的python getattr和getattribute_python中__getattr__和__getattribute__区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 双向dcdc变换器simulink仿真_
- 下一篇: python汉诺塔问题_Python汉诺