Python魔法方法__getattr__和__getattribute__詳解

在Python中有這兩個魔法方法容易讓人混淆:__getattr__和getattribute。一般咱們會定義__getattr__而歷來不會定義getattribute,下面咱們來看看這兩個的區別。python

__getattr__魔法方法

class MyClass:

    def __init__(self, x):
        self.x = x

    def __getattr__(self, item):
        print('{}屬性爲找到!'.format(item))
        return None

>>> obj = MyClass(1)
>>> obj.x
1
>>> obj.y
y屬性爲找到!
None

咱們定義一個MyClass類,設置一個實例屬性爲x,值爲1。obj爲這個類的實例,獲取obj.x返回1,而獲取obj.y發現屬性找不到,緣由是obj的實例變量中不包含y,找不到某屬性時會調用__getattr__方法。框架

**調用__getattr__詳細過程以下:**
obj.attrcode

  1. 首先會在對象的實例屬性中尋找,找不到執行第二步
  2. 來到對象所在的類中查找類屬性,若是還找不到執行第三步
  3. 來到對象的繼承鏈上尋找,若是還找不到執行第四步
  4. 調用obj.__getattr__方法,若是用戶沒有定義或者仍是找不到,拋出AttributeError異常,屬性查找失敗!
class MyClass:

    def __init__(self, x):
        self.x = x
>>> obj = MyClass(1)
>>> obj.y

AttributeError: 'MyClass' object has no attribute 'a'

如上代碼,沒有定義__getattr__魔法方法,又找不到屬性,就會拋出異常orm

__getattribute__魔法方法

當咱們調用對象的屬性時,首先會調用__getattribute__魔法方法。對象

obj.x
obj.__getattribute__(x)

如上代碼,這兩個代碼實際上是等價的。當__getattribute__查找失敗,就會去調用__getattr__方法。繼承

代碼演示遞歸

class MyClass:

    def __init__(self, x):
        self.x = x

    def __getattribute__(self, item):
        print('正在獲取屬性{}'.format(item))
        return super(MyClass, self).__getattribute__(item)
>>> obj = MyClass(2)
>>> obj.x
正在獲取屬性x
2

咱們使用__getattribute__魔法方法時,要返回父類的方法,否則很難寫對
下面代碼是一個陷阱,會產生無限遞歸get

class MyClass:

    def __init__(self, x):
        self.x = x

    def __getattribute__(self, item):
        print('正在獲取屬性{}'.format(item))
        return self.item
        
>>> obj = MyClass(2)
>>> obj.x
  File "xxx", line 11, in __getattribute__
    print('正在獲取屬性{}'.format(item))
RecursionError: maximum recursion depth exceeded while calling a Python object

上面的代碼看起來彷佛是對的,但卻調入了無限遞歸的陷阱,至關於it

def __getattribute__(self, item):
    print('正在獲取屬性{}'.format(item))
    return self.__getattribute__(item)

要十分警戒。io

另外,內置的getattr和hasattr也會觸發這個魔法方法

>>> getattr(obj, 'x', None)
正在獲取屬性x
2
>>> hasattr(obj, 'x', None)
正在獲取屬性x
True

其餘細節須要注意

class MyClass:

    x = 999
    
    def __init__(self, x):
        self.x = x

    def __getattribute__(self, item):
        print('正在獲取屬性{}'.format(item))
        return super(MyClass, self).__getattribute__(item)

上面代碼中,定義了一個類屬性x和一個實例屬性x,這兩個屬性同名,根據Python語法規則,當對象獲取屬性x的時候,首先會在實例屬性中尋找若是找不到纔回去類屬性中查找

>>> obj = MyClass(2)
>>> print(obj.x)
正在獲取屬性x
2
>>> del obj.x  #刪除了實例屬性x
>>> print(obj.x)  #此時訪問的是類屬性
正在獲取屬性
999

這樣就能印證了上面所說__getattribute__的查找順序。一般該方法在框架中可能會用到,通常狀況下無需使用。

相關文章
相關標籤/搜索