重载__getattr__方法对类及其实例未定义的属性有效。如果访问的属性存在,就不会调用__getattr__方法。这个属性的存在,包括类属性和实例属性
class ClassA: 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__方法
class ClassA: 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异常。
class ClassA: 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__方法也会有这个问题)。
示例代码(无限递归):
class ClassA: 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__取值的方式来避免无限递归
class ClassA: 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()方法
class ClassA: 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
异常,属性查找失败
class MyClass: 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__!!
其他细节需要注意
_getattribute__
的查找顺序
class MyClass: x = 999 #类属性x def __init__(self, x): #形参x self.x = x #实例属性x def __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__
的查找顺序。通常该方法在框架中可能会用到,一般情况下无需使用
另外的
__setattr__(self, name, value)
定义当一个属性被设置时的行为
__delattr__(self, name)
定义当一个属性被删除时的行为
原文地址:https://www.cnblogs.com/hemengjita/p/12288429.html