python 中有2个获取对象属性的方法__getattribute__ 和__getattr__方法比较容易混淆,
接下来说明下这2个魔方方法的区别及用法:
__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
分析:首先我们定义了一个类并设置了一个实例属性x,重新定义__getattr__这个方法。在执行的时候,首先实例化obj这个对象,传递了一个1的实参,我们输出obj.x查看属性值,有找到并且为1;再次查找一个不存在的属性y,然后自动调用__getattr__这个方法,然后打印出没找到属性。(这里如果不重新定义__getattr__,会报AttributeError
异常)。
class MyClass:
def __init__(self, x):
self.x = x
>>> obj = MyClass(1)
>>> obj.y
AttributeError: 'MyClass' object has no attribute 'y'
**调用__getattr__详细过程如下:**obj.attr
- 首先会在对象的实例属性中寻找,找不到执行第二步
- 来到对象所在的类中查找类属性,如果还找不到执行第三步
- 来到对象的继承链上寻找,如果还找不到执行第四步
- 调用
obj.__getattr__
方法,如果用户没有定义或者还是找不到,抛出AttributeError
异常,属性查找失败!
__getattribute__方法
当我们调用对象的属性时,首先会调用__getattribute__
魔法方法。
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
**注意上面例子标红处,需要返回父类的方法,若是返回其他很容易产生无限递归:
例:
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
调用此方法查找属性的过程与__getattr__方法一致。
====================================================================================
总的来说:
这2个方法作用都是相同的,都是查找对象的属性值,查询不到抛出异常。一般我们想自定义查找属性方法的时候,用__getattr__方法比较多,__getattribute__方法在调用对象属性的时候首先调用,而当__getattribute__
查找失败,就会去调用__getattr__
方法。