__getattribute__ python __getattribute__炸裂真相:Python属性查找竟藏着这种魔鬼逻辑
全新采用了新式类, 摒弃了旧式类, 身为新式类的一项特性有着极为奇妙的功效。查阅一些博客以及文章之时, 察觉到欲要透彻领会和的差异, 事实上得领会中属性的查找次序、描述器()、、、等相关知识。
首要的是要明确指出, 要是存在一个类, 其仅仅定义了方法,那么这种类就被称作non - data(非资料描述器), 要是这个类既把某个东西和另一个东西都定义了, 那么它就被称作data(资料描述器), 具体的情况能够在我的那里看到, 这便于理解。接下来进行实例属性查找顺序的介绍。假定t = T(), t.at的查找顺序是像下面这样的:。
1、如果是自动产生的属性,返回,否则进行第2步
2、要是“at”在T或者它的父类以及祖先类里面出现了(表示“at”是类属性, 不是仅属于t的实例属性), 而且at是一个data, 那么就优先调用它的方法。倘若是非data或者不存在该属性情况, 那就进行第2步。
3、找寻一下实例t之中是不是存在at属性, 要是有的话就予以返回, 要是没有的话那就进入到第3步。
4、查找t的父类之中, 是否存在at属性;还要查找t的祖先类之中以及别处, 是否存在at属性;要是没有at属性, 那么就执行第4步;要是有at属性, 那么就执行如下步骤:
4.1处的at属于一个non - data , 去调用它的方法 , 要是并非如此那就去执行3.2。
4.2 返回'at'
5、如果实例t的父类中有方法,则调用该方法,没有则抛出。
需留意, 每当类或者实例对属性进行调用之际, 都会被毫无条件地在最起初的时候予以调用。下面的代码篇幅稍长, 烦请耐心去查看。
class Descriptor: # 定义描述器的类 def __get__(self, instance, owner): # get方法用于返回实例的a属性 print('3 get called,', 'instance is', instance, ',owner is', owner) return instance.a def __set__(self, instance, value): # set方法用于修改实例的a属性 print('4 set called,', 'instance is', instance, ',value is', value) instance.a = value**2 def __getattribute__(self, item): print('5 Des getattribute called, item is %s' % item) class NotDescriptor: # 定义non-data descriptor def __get__(self, instance, owner): print('6 get called,', 'instance is', instance, ',owner is', owner) return instance.a - 100 class T: desc = Descriptor() # 类属性,一个资料描述器 data descriptor Not_Desc = NotDescriptor() # 类属性,一个非资料描述器 non-data descriptor def __init__(self): self.a = 123 self.not_desc = 'instance not_desc' def func(self): return 'T func' def __getattribute__(self, item): print('1 getattribute called, item is ', item) return object.__getattribute__(self, item) def __getattr__(self, item): print('2 getattr called, item is', item) raise AttributeError('NO such attr %s' % item)下面的这些测试, 全都运用上方的那代码, 每一回的输出操作, 都已进行过重置, 目的是防止出现混淆。
1 getattribute called, item is func T func # 实例的函数 1 getattribute called, item is a 123 # 实例的属性 ================================================== 4 set called, instance is <__main__.T object at 0x000001E55B630240> ,value is 2 # set方法前不调用getattribute。instance是拥有该描述器类的一个实例。value是要设置的值。 ================================================== 1 getattribute called, item is desc 3 get called, instance is <__main__.T object at 0x000001E55B630240> ,owner is # get方法前还是优先调用getattribute,instance是拥有该描述器对象的一个实例。owner是拥有者本身 1 getattribute called, item is a t.a = 4 # 2**2 = 4可以看到, 在调用实例的属性以及方法的时候, 都会毫无条件地率先调用方法, 然而set方法是不会被调用的。此外, 代码借助描述器的get方法以及set方法, 实际上已然达成了类似于@的运用情况。当然了, 装饰器实在是基于描述器而得以实现的, 要是对装饰器并不明白的话, 是能够去查看我的。
下面继续使用上方代码验证、非描述器的执行顺序。
t = T() print(t.not_desc) --------结果如下------ 1 getattribute called, item is not_desc instance not_desc回想前面所提及的属性查找顺序, 在类当中被找到了, 然而并非如此, 所以执行第二步, 于实例的里面发现了该属性, 进而返回。
t = T() print(t.Not_Desc) --------结果如下------ 1 getattribute called, item is Not_Desc 6 get called, instance is <__main__.T object at 0x0000017F70A006A0> ,owner is # 这里是NonDataDescriptor中的get方法 1 getattribute called, item is a 23 # a的初始值为123 由get方法返回的是123-100=23依据我们先前提及的顺序, 属性于类之中被找到, 然而并非data , 且未在实例的dict里找到, 于是进展到了4.1步骤, 发觉它是一个non-data , 随后便执行了其中的get方法。
t = T() print(t.bbbbbb) # T类中没有定义该属性 ------结果如下---------- 1 getattribute called, item is bbbbbb 2 getattr called, item is bbbbbb Traceback (most recent call last): File "C:/省略/.py", line 40, in print(t.bbbbbb) File "C:C:/省略/.py.py", line 36, in __getattr__ raise AttributeError('NO such attr %s' % item) AttributeError: NO such attr bbbbbb可见, 当我们去访问一个并未被定义的属性之时, 依旧会率先进行调用, 依据属性查找的原则, 在实例之中以及类里面都没能找到这个属性, 于是便会执行。到了此地, 我们也就知晓了其属性的查找顺序, 以及执行顺序。
当回过头去看时, 在调用类以及实例的属性之际会进行无条件调用, 所以能够将其用于权限鉴别这样的操作, 还能用于日志记录等方面;进而在属性未被找到的情形下会予以执行, 所以能够借助它来开展一些兜底的操作, 由此可见。
另外, 要留意重写期间的循环陷阱, 返回语句得写成.( ) , 别运用 self.xxx , 不然 self 等同于再次指向自身, 进而会无条件展开调用, 由此踏入无限循环。
最后的我们查看类和实例的属性,看看又什么不同
t = T() print(t.__dict__) print(T.__dict__) ------结果如下-------- 1 getattribute called, item is __dict__ {'a': 123, 'not_desc': 'instance not_desc'} {'__module__': '__main__', 'desc': <__main__.Descriptor object at 0x0000021FD20C0128>, 'Not_Desc': <__main__.NotDescriptor object at 0x0000021FD20C0160>, '__init__': , 'func': , '__getattribute__': , '__getattr__': , '__dict__': , '__weakref__': , '__doc__': None}由此能够看出, 实例t具备的仅仅是在init里所定义的那些属性, 然而, 类T当中才存在着诸如描述器以及非描述器之类的属性。
