1. 程式人生 > >淺談__getattribute__與__getattr__

淺談__getattribute__與__getattr__

參加校招的時候被面試官問到了,沒能答上,主要是混淆了__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.NAMEa.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__方法在子類中起的作用