__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的实现感觉还是挺抽象化的,只需要绑定相应的实例对象和要查找的属性名称就行