python getattr和getattribute_python中__getattr__和__getattribute__区别
重載__getattr__方法對類及其實例未定義的屬性有效。如果訪問的屬性存在,就不會調用__getattr__方法。這個屬性的存在,包括類屬性和實例屬性
classClassA:
x= 'a'
def __init__(self):
self.y= 'b'
def __getattr__(self, item):return '__getattr__'
if __name__ == '__main__':
a=ClassA()print(a.x)#輸出結果 a
#使用實例直接訪問實例存在的屬性時,不會調用__getattr__方法
print(a.y) #輸出結果 b
#使用實例直接訪問實例不存在的屬性時,會調用__getattr__方法
print(a.z) #輸出結果 __getattr__
__getattribute__僅在新式類中可用,重載__getattrbute__方法對類實例的每個屬性訪問都有效,無論屬性存不存在都會先調用__getattribute__方法
classClassA:
x= 'a'
def __init__(self):
self.y= 'b'
def __getattribute__(self, item):return '__getattribute__'
if __name__ == '__main__':
a=ClassA()#使用實例直接訪問存在的類屬性時,會調用__getattribute__方法
print(a.x) #輸出結果 __getattribute__
#使用實例直接訪問實例存在的實例屬性時,會調用__getattribute__方法
print(a.y) #輸出結果 __getattribute__
#使用實例直接訪問實例不存在的實例屬性時,也會調用__getattribute__方法
print(a.z) #輸出結果 __getattribute__
當同時定義__getattribute__和__getattr__時,__getattr__方法不會再被調用,除非顯示調用__getattr__方法或引發AttributeError異常。
classClassA:def __getattr__(self, item):print('__getattr__')def __getattribute__(self, item):print('__getatttribute__')if __name__ == '__main__':
a=ClassA()
a.x
運行結果__getatttribute__
由于__getattr__只針對未定義屬性的調用,所以它可以在自己的代碼中自由地獲取其他屬性,
而__getattribute__針對所有的屬性運行,因此要十分注意避免在訪問其他屬性時,再次調用自身的遞歸循環。死循環!!
當在__getattribute__代碼塊中,再次執行屬性的獲取操作時,會再次觸發__getattribute__方法的調用,代碼將會陷入無限遞歸,直到Python遞歸深度限制(重載__setter__? __setattr__方法也會有這個問題)。
示例代碼(無限遞歸):
classClassA:
x= 'a'
def __getattribute__(self, item):print('__getattribute__')return self.item #再次出現屬性的獲取操作,會再次觸發__getattribute__的調用
#相當于return self.__getattribute__(item)
if __name__ == '__main__':
a=ClassA()
a.x
運行結果,達到最大遞歸深度
ecursionError: maximum recursion depth exceeded
也沒辦法通過從__dict__取值的方式來避免無限遞歸(重寫__setattr__可以通過__dict__取值的方式來避免無限遞歸)
classClassA:
x= 'a'
def __getattribute__(self, name):return self.__dict__[name] #__dict__魔法方法可以查看對象的屬性,返回一個字典,鍵代表屬性名 ,這樣再次出現屬性獲取的操作,會再次觸發__getattribute__if __name__ == '__main__':
a=ClassA()
a.x#無限遞歸
為了避免無限遞歸,應該把獲取屬性的方法?__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
調用__getattr__詳細過程如下:
obj.attribute
首先會在對象的實例屬性中尋找,找不到執行第二步
來到對象所在的類中查找類屬性,如果還找不到執行第三步
來到對象的繼承鏈上尋找,如果還找不到執行第四步
調用obj.__getattr__方法,如果用戶沒有定義或者還是找不到,拋出AttributeError異常,屬性查找失敗
classMyClass:def __init__(self, x):
self.x=x>>> obj = MyClass(1)>>>obj.y
AttributeError:'MyClass' object has no attribute 'a'
如上代碼,沒有定義__getattr__魔法方法,又找不到屬性,就會拋出異常
調用__getattrIbute__方法
當我們調用對象的屬性時,首先會調用__getattribute__魔法方法。無論對象存不存在;當__getattribute__查找失敗,就會去調用__getattr__方法。
obj.x
obj.__getattribute__(x)
這兩個代碼其實是等價的
使用__getattribute__魔法方法時,要返回父類的方法,(super函數)不然很難寫對!!會導致無限遞歸!
另外,內置的bif? ?getattr和hasattr也會觸發這個魔法方法__getattribute__!!
其他細節需要注意
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,這兩個屬性同名,根據Python語法規則,當對象獲取屬性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
運行結果:>>> 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 會先調用 __getattribute__() 魔法方法,打印 2;然后調用 super().__getattribute__(),找不到屬性名 x,因此會緊接著調用 __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()?函數不帶參數時,返回當前范圍內的變量、方法和定義的類型列表;帶參數時,返回參數的屬性、方法列表。如果參數包含方法__dir__(),該方法將被調用。如果參數不包含__dir__(),該方法將最大限度地收集參數信息)
3. 初學常犯錯誤沒有“觀前顧后”!
例如:編寫一個 Counter 類,用于實時檢測對象有多少個屬性。
classCounter:def __init__(self):
self.counter=0 # 屬性賦值,這里會觸發 __setattr__ 調用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()
運行結果:
AttributeError: 'Counter' object has no attribute 'counter'
正確代碼:
classCounter:def __init__(self):
super().__setattr__('counter', 0) #調用基類的賦值魔法方法__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)
定義當一個屬性被設置時的行為
__delattr__(self, name)
定義當一個屬性被刪除時的行為
總結
以上是生活随笔為你收集整理的python getattr和getattribute_python中__getattr__和__getattribute__区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 双向dcdc变换器simulink仿真_
- 下一篇: 沪农商行是科创板吗 在股票版块里沪农商行