二. __getattribute__
 
1. 先看看 : __getattr__
 
 
class Bar:
    def __getattr__(self, item):
        print("没找到,触发了我")
bb = Bar()
bb.name    # 没找到,触发了我
 
2.__getattribute__
 
 
class Bar:
    def __init__(self,name):
        self.name = name
    def __getattribute__(self, item):
        print(f"无论找没找到,都触发了我-->{item}")
bb = Bar("shawn")
bb.name  # 无论找没找到,都触发了我-->name
bb.age   # 无论找没找到,都触发了我-->age
 
3.两者同时存在
 
🍔两者同时存在
class Bar:
    def __init__(self,name):
        self.name = name
    def __getattr__(self, item):
        print("没找到,触发了我")
    def __getattribute__(self, item):
        print(f"无论找没找到,都触发了我-->{item}")
bb = Bar("shawn")
bb.name  # 无论找没找到,都触发了我-->name
bb.age   # 无论找没找到,都触发了我-->age
🍔设置异常
class Bar:
    def __init__(self,name):
        self.name = name
    def __getattr__(self, item):
        print("没找到,触发了我")
    def __getattribute__(self, item):
        print(f"无论找没找到,都触发了我-->{item}")
        raise AttributeError('让小弟接管')  # 设置异常,直接交给__getattr__
bb = Bar("shawn")
bb.name
'''
无论找没找到,都触发了我-->name
没找到,触发了我
'''
bb.age
'''
无论找没找到,都触发了我-->age
没找到,触发了我
'''
 
 
 - [对象] . [属性]的调用顺序 : 先执行 __getattribute__--->去类的名称空间找--->__getattr__(本质是去对象自己的名称空间找)
- [对象] . [属性]的查找顺序 : 对象自己--->类--->父类--->父类
4.总结
 
 
 - __getattribute__方法优先级比- __getattr__高
- 没有重写__getattribute__的情况下, 默认使用的是父类的__getattribute__方法
- 只有在使用默认__getattribute__方法中找不到对应的属性时,才会调用__getattr__
- 如果是对不存在的属性做处理,尽量把逻辑写在__getattr__方法中
- 如果非得重写__getattribute__方法,需要注意两点: 
   - 第一是避免.操作带来的死循环
- 第二是不要遗忘父类的__getattribute__方法在子类中起的作用