__getattr__比较好理解~
首先明白__getattr__是什么。
**getattr()**函数会在你试图读取一个不存在的属性时,引发AttributeError异常。
下面看一段代码段。
class testclass:
count = 0
name = []
def __init__(self,name,count):
self.count += count
self.name = name
def __getattr__(self, item):
print('无此项,出错')
m = testclass('wang',100)
m.addr
m.addr='上海'
print(m.name)
print(m.addr)
为了简化代码,就没有涉及__del__(self)等,比较好理解,这个就简单定义了一个类,里面有name和count两个属性,但是没有addr,这时候运行的结果如下:
无此项,出错
wang
上海
这个很好理解接下来看__getattribute__。
getattribute() 是在查找真正要访问的属性之前就被调用了,无论该属性是否存在。可以跟__getattr__有个区别了吧(在定义层面)。
使用__getattribute__()要特别注意,因为如果你在它里面不知何故再次调用了__getattribute__(),你就会进入无穷递归。为了避免这种情况,如果你想要访问任何它需要的属性,应该总是调用祖先类的同名方法:比如super(obj, self).getattribute(attr)。
上面这话怎么理解呢?看下面这段代码:
class testclass:
count = 0
name = []
def __init__(self,name,count):
self.count += count
self.name = name
def __getattr__(self, item):
print('无此项,出错')
def __getattribute__(self, item):
return 1
m = testclass('wang',100)
m.addr
m.addr='上海'
print(m.name)
print(m.addr)
del m.addr
大家运行上面这个代码发现惊人的结果:
1
1
对的没有看错都变成了1。
换种更直观的:
class TestMain:
def __init__(self):
print('TestMain:__init__')
self.a = 1
def __getattr__(self, item):
print('TestMain:__getattr__')
return 2
def __getattribute__(self, item):
print('TestMain:__getattribute__')
return 3
if __name__ == '__main__':
t = TestMain()
print(t.a)
print(t.b)
结果是
TestMain:__init__
TestMain:__getattribute__
3
TestMain:__getattribute__
3
这时候就可以看出来了吧,无论是访问存在的t.a还是不存在的t.b,都访问到了__getattribute__这个函数,也就是说,只要定义了这个函数,那么属性的访问,都会走到这个函数里面。
我们再看下面这段代码:
class TestMain:
def __init__(self):
print('TestMain:__init__')
self.a = 1
def __getattr__(self, item):
print('TestMain:__getattr__')
return 2
def __getattribute__(self, item):
print('TestMain:__getattribute__')
if item == 'c':
raise AttributeError
return 3
if __name__ == '__main__':
t = TestMain()
print(t.a)
print(t.b)
print(t.c)
我们知道只要定义了__getattribute__函数,就肯定执行这个函数来获取属性,这次我们增加了判断如果访问c这个属性,我们抛出异常,最后的结果是:
TestMain:__init__
TestMain:__getattribute__
3
TestMain:__getattribute__
3
TestMain:__getattribute__
TestMain:__getattr__
2
也就是说,如果__getattribute__抛出了AttributeError异常,那么会继续访问__getattr__函数的。
那么有人会问,那该怎么办呢
class TestMain:
def __init__(self):
print('TestMain:__init__')
self.a = 1
self.b = 2
def __getattr__(self, item):
print('TestMain:__getattr__')
return 3
def __getattribute__(self, item):
print('TestMain:__getattribute__')
return super().__getattribute__(item)
if __name__ == '__main__':
t = TestMain()
print(t.a)
print(t.b)
print(t.c)
是不是就是咱们刚开始提到的,那这个运行出来结果是什么呢?
```python
TestMain:__init__
TestMain:__getattribute__
1
TestMain:__getattribute__
2
TestMain:__getattribute__
TestMain:__getattr__
3
注意这行:
def __getattribute__(self, item):
return super().__getattribute__(item)
我们发现每次访问类的属性的时候,
都会有TestMain:__getattribute__输出,这就验证了我们刚刚说的只要定义了__getattribute__函数,就肯定执行这个函数来获取属性。
但是加了这两行后就可以正常输出各个属性的值,也可以正常映射到__getattr__方法中。
那么这两句话是什么意思的?其实就是返回父代这个类的属性的值,以防止__getattribute__来篡改属性值从而返回错误的结果。
虽然本例并没有实现继承,但是python中默认所有的类都继承自object(被称为超类可以省略的),所以就直接调用父类同名的方法就可以了!
总的来说:
1、如果定义了__getattribute__,那么无论访问什么属性,都是通过这个函数获取,包括方法,t.f()这种也是访问的这个函数,此时这个函数应该放回一个方法,如果像例子中,仍然返回一个数字,你会获得一个TypeError: ‘int’ object is not callable错误。
2、只要定义了__getattribute__方法,不管你访问一个存在的还是不存在的属性,都由这个方法返回,比如访问t.a,虽然a存在,但是只要定义了这个访问,那么就不是访问最开始的a了
3、如果__getattribute__抛出了AttributeError异常,并且定了了__getattr__函数,那么会调用__getattr__这个函数,不论这个属性到底是不是存在
4、如果想保持属性的值,则可以
return super().getattribute(item)
4、也就是说属性访问的一个大致优先级是:getattribute > getattr > dict
__setattr__可以限制或者管理对象成员的添加或修改操作。
其参数self接收当前对象,第二个参数接收设置的成员名称字符串,第三个参数接收设置的值
__setattr__同理,有了上面的基础,接下来直接看这个(就不循序渐进了,都是父类同名方法):
class TestMain:
def __init__(self):
print('TestMain:__init__')
self.a = 1
self.b = 2
def __getattr__(self, item):
print('TestMain:__getattr__')
return 3
def __getattribute__(self, item):
print('TestMain:__getattribute__')
return super().__getattribute__(item)
def __setattr__(self, key, value):
print('TestMain:__setattr__')
super().__setattr__(key,value)
if __name__ == '__main__':
t = TestMain()
print(t.a)
print(t.b)
print(t.c)
咋们应该知道super().setattr(key,value)什么意思啦(避免输出错的属性值,__getattribute__中提到有~),来看看结果:
TestMain:__init__
TestMain:__setattr__
TestMain:__setattr__
TestMain:__getattribute__
1
TestMain:__getattribute__
2
TestMain:__getattribute__
TestMain:__getattr__
3
还没有完,因为到这里可能有的小伙伴还在懵逼状态,我们这里放慢速度,将主函数改为:
if __name__ == '__main__':
t = TestMain()
# print(t.a)
# print(t.b)
# print(t.c)
结果为
TestMain:__init__
TestMain:__setattr__
TestMain:__setattr__
没错还是两个TestMain:setattr
好了不做消融试验了,其实这里的TestMain:__setattr__出现的次数是取决于你类中定义的属性个数,这里是a,b,也就是两个,小伙伴们可以尝试一下~
这里可以理解为,只要访问属性时,这么说不准确,也就是在定义对象时包含属性的的个数就是访问__setattr__的次数。
本文到这里就结束了,希望能对这3个有个了解和初步的认识。