类的内置方法__getattribute__、__getattr__、__setattr__
__getattr__:若访问属性不存在时则会调用该魔法方法。
首先理解__getattr__的用法,看代码:
class Test(object): def __init__(self,name): self.name = name def __getattr__(self, item): print(item,"Not Found") if __name__ == '__main__': test = Test('小明') print(test.getattr)
getattr Not Found
None
由结果上看,test = Test('小明')对类进行实例化,print(test.getattr)打印了类的实例属性(注:不是访问__getattr__方法),结果首先输出getattr Not Found,可见首先访问__getattr__方法,之后打印输出为None(属性不存在),假如未定义__getattr__方法时在查找不存在的属性时则会爆出AttributeError,可见__getattr__是进行错误异常处理的处理方法,可用来对错误异常处理方面进行自定制。
__setattr__:若对一个实例的属性进行赋值时则会自动调用该方法。
接下来看代码:
class Test(object): def __init__(self,name): self.name = name def __getattr__(self, item): print(item,"Not Found") def __setattr__(self, key, value): print(key,'已被赋值为',value) self.__dict__[key] = value if __name__ == '__main__': test = Test('小明') test.name = '小张' print(test.name)
name 已被赋值为 小明
name 已被赋值为 小张
小张
test = Test('小明')对类进行实例化,并且初始化self.name = name,为一次赋值操作,所以会调用__setattr__,输出为name 已被赋值为小明。再看test.name = 小张',再次给实例属性name重新赋值,再次调用__setattr__。
总结:对一个属性进行赋值会调用__setattr__方法。
拓展:
class A: def __init__(self): self.age = 6 self.gender = 'male' class Test(A): def __init__(self,name): self.name = name def __getattr__(self, item): # print(super(Test,self).__init__()) super(Test,self).__init__() print(item,"Not Found") def __setattr__(self, key, value): print(key,'已被赋值为',value) self.__dict__[key] = value if __name__ == '__main__': test = Test('小明') print(test.age) print(test.__dict__) print(test.age)
name 已被赋值为 小明 age 已被赋值为 6 gender 已被赋值为 male age Not Found None {'name': '小明', 'age': 6, 'gender': 'male'} 6
当访问Test的age属性时是没找到的,所以会执行子类的__getattr__方法,但是此例中在__getattr__中调用超类的__init__方法,并对age和gender进行初始化。由结果可以看出,在子类调用父类的方法并对属性赋值时,还是会调用子类的__setattr__方法。由最后输出的字典也可以看出,父类的age和gender已经被添加到子类的__dict__中,并且以后子类也存在age和gender属性。
总结:1、子类中调用父类方法,若对属性进行初始化或赋值等操作,则会调用子类__setattr__方法,同理若在父类中找不到该属性值则会调用__getattr__。
2、调用__setattr__方法时,会把key和value当作参数传进,届时可以把item添加进子类属性字典,以便后续操作可以访问。
__getattibute__:
-
1 调用属性会触发该功能,属性存在则会返回相应的值;
-
2 如果属性不存在则会抛出异常AttributeError,所以可以自定义异常信息
-
3 存在
__getattr__,若有异常出现则会传递给__getattr__用来接收,执行操作
class Test(object): def __getattribute__(self, item): return super(Test, self).__getattribute__(item) if __name__ == '__main__': test = Test() print(test.age)
Traceback (most recent call last): File "XXX", line 220, in <module> print(test.age) File "XXX", line 215, in __getattribute__ return super(Test, self).__getattribute__(item) AttributeError: 'Test' object has no attribute 'age'
由结果可知若访问属性不存在时发出AttributeError错误。
class Test(object): def __getattribute__(self, item):
print('首先访问我!') return super(Test, self).__getattribute__(item) def __getattr__(self, item): print(item,"Not Found") if __name__ == '__main__': test = Test() print(test.age)
首先访问我!
age Not Found
None
再次编写代码可知,当代码添加入__getattr__方法时,代码不会报错,且首先访问__getattribute__。
结论:当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
拓展:
class Test(object): def __init__(self,name): self.name = name def __getattribute__(self, item): print('首先访问我') # print(self.__dict__) #在访问__dict__时同时首先访问__getattribute__引起无 限递归 return super(Test, self).__getattribute__(item) # return self.__dict__(item) def __getattr__(self, item): print(item,"Not Found") if __name__ == '__main__': test = Test("小明") print(test.name)
因为getattribute在访问属性的时候一直会被调用,自定义的getattribute方法里面同时需要返回相应的属性,通过self.__dict__取值会继续向下调用getattribute,造成循环调用。
这里通过调用绑定的super对象来获取对应的属性,对新式类来说其实和object.__getattribute__(self, item)一样的道理:
默认情况下自定义的类会从object继承getattribute方法,对于属性的查找是完全能用的
getattribute的实现感觉还是挺抽象化的,只需要绑定相应的实例对象和要查找的属性名称就行。
以下是新式类关于__getattribute__的官方文档:
Called unconditionally to implement attribute accesses for instances of the class. If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError. This method should return the (computed) attribute value or raise an AttributeError exception. In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).
https://docs.python.org/3/reference/datamodel.html#more-attribute-access-for-new-style-classes
浙公网安备 33010602011771号