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