淺談__getattribute__與__getattr__
阿新 • • 發佈:2018-12-13
參加校招的時候被面試官問到了,沒能答上,主要是混淆了__getattribute__和__getattr__兩個魔法方法。因此這裡筆記一下。
首先我們看看如何訪問一個物件的屬性:
class A(object):
def __init__(self):
self.name = "Bob"
self.age = 18
self.gender = "male"
if __name__ == "__main__":
a = A()
print(a.name)
print(a.age)
print(a.gender)
# 輸出:
#Bob
#18
#male
如果我們想改變訪問屬性的邏輯,如執行a.age
語句並非返回18,而是返回問年齡是不禮貌的行為。這裡就可以用__getattribute__方法攔截屬性,實現我們想要實現的邏輯。
class A(object):
def __init__(self):
self.name = "Bob"
self.age = 18
self.gender = "male"
def __getattribute__(self, attr):
# 攔截age屬性
if attr == "age":
return "問年齡是不禮貌的行為"
# 非age屬性執行預設操作
else:
return object.__getattribute__(self, attr)
if __name__ == "__main__":
a = A()
print(a.age)
print(a.name)
print(a.gender)
# 輸出:
#問年齡是不禮貌的行為
#Bob
#male
事實上,在例項化的物件進行.
操作的時候(形如:a.xxx/a.xxx()
),都會自動去呼叫__getattribute__方法。但是,如果某個屬性在__getattribute__方法中未能找到,此時會呼叫__getattr__方法。
如我們訪問物件a中不存在的屬性,會得到異常。比方執行print(a.NAME)
語句,執行結果如下:
AttributeError: 'A' object has no attribute 'NAME'
但我就想無論是a.NAME
,a.Name
,還是a.NaME
等等,即n a m e
(包含大小寫)四個元素組成的屬性,訪問結果都同a.name
一樣,可以做如下處理:
class A(object):
def __init__(self):
self.name = "Bob"
self.age = 18
self.gender = "male"
def __getattr__(self, attr):
return eval("self."+attr.lower()) #即:再次去執行__getattribute__方法
if __name__ == "__main__":
a = A()
print("a.name -> {}".format(a.name))
print("a.NAME -> {}".format(a.NAME))
print("a.Name -> {}".format(a.Name))
print("a.NaME -> {}".format(a.NaME))
# 輸出:
#a.name -> Bob
#a.NAME -> Bob
#a.Name -> Bob
#a.NaME -> Bob
總結:
1.__getattribute__
方法優先順序比__getattr__
高
2.只有在__getattribute__
方法中找不到對應的屬性時,才會呼叫__getattr__
3.如果是對不存在的屬性做處理,儘量把邏輯寫在__getattr__
方法中
4.如果非得重寫__getattribute__
方法,需要注意兩點:第一是避免.
操作帶來的死迴圈;第二是不要遺忘父類的__getattribute__
方法在子類中起的作用