【python】python中__getattr__,__getattribute__,__setattr__等魔法方法
通常情况下,我们在访问类或者实例对象的时候,会牵扯到一些属性访问的魔法方法,主要包括:
① __getattr__(self, name): 访问不存在的属性时调用
② __getattribute__(self, name):访问存在的属性时调用(先调用该方法,查看是否存在该属性,若不存在,接着去调用①)
③ __setattr__(self, name, value):设置实例对象的一个新的属性时调用
④ __delattr__(self, name):删除一个实例对象的属性时调用
为了验证以上,有如下代码:
class Test: def __getattr__(self, item): print("calling __getattr__...") def __getattribute__(self, item): print("calling __getattribute__...") def __setattr__(self, key, value): print("calling __setattr__...") def __delattr__(self, item): print("calling __delattr__") t = Test() t.x # result: calling __getattribute__...
如上述代码所示,x并不是Test类实例 t 的一个属性,首先去调用__getattribute__()方法,得知该属性并不属于该实例对象;但是,按照常理,t.x应该打印'calling __getattribute__...'和'calling __getattr__',但是实际情况并非如此,为什么呢?难道以上python的规定无效吗?不要着急,听我慢慢道来!
实例对象属性寻找的顺序如下:
① 首先访问 __getattribute__() 魔法方法(隐含默认调用,无论何种情况,均会调用此方法)
② 去实例对象t中查找是否具备该属性: t.__dict__ 中查找,每个类和实例对象都有一个 __dict__ 的属性
③ 若在 t.__dict__ 中找不到对应的属性, 则去该实例的类中寻找,即 t.__class__.__dict__
④ 若在实例的类中也招不到该属性,则去父类中寻找,即 t.__class__.__bases__.__dict__中寻找
⑤ 若以上均无法找到,则会调用 __getattr__ 方法,执行内部的命令(若未重载 __getattr__ 方法,则直接报错:AttributeError)
以上几个流程,即完成了属性的寻找。
问题就出在了步骤的第④步,因为,一旦重载了 __getattribute__() 方法,如果找不到属性,则必须要手动加入第④步,否则无法进入到 第⑤步 (__getattr__)的。
验证一下以上说法是否正确:
class Test(object): def __getattr__(self, item): print("calling __getattr__...") def __getattribute__(self, item): print("calling __getattribute__...") object.__getattribute__(self,item) def __setattr__(self, key, value): print("calling __setattr__...") def __delattr__(self, item): print("calling __delattr__") t = Test() t.x #result calling __getattribute__... calling __getattr__...
方法二:采用super()方法
class Test(object): def __getattr__(self, item): print("calling __getattr__...") def __getattribute__(self, item): print("calling __getattribute__...") # object.__getattribute__(self,item) super(Test, self).__getattribute__(item) def __setattr__(self, key, value): print("calling __setattr__...") def __delattr__(self, item): print("calling __delattr__") t = Test() t.x #result calling __getattribute__,,, calling __getattr__...
以上介绍完毕,再介绍下__setattr__和__delattr__
再补充一点:
class Test: def __init__(self): self.count = 0 def __settattr__(self, name, value) print('__settattr__') self.count +=1 # t = Test() # __setattr__ Traceback (most recent call last): File "<pyshell#364>", line 1, in <module> t=Test() File "<pyshell#363>", line 3, in __init__ self.count = 0 File "<pyshell#363>", line 6, in __setattr__ self.count += 1 AttributeError: 'Test' object has no attribute 'count'
为什么会出现上述情况呢?我还没有调用__setattr__()呢,只是单纯的定义了一个实例而已? @_@(咋回事?幻觉吗)
看报错信息很容易明白,这是因为:
① __init__()时,给内部属性 self.count进行了赋值;
② 赋值默认调用 __setattr__() 方法
③ 当调用 __setattr__()方法时,首先打印 '__setattr__'字符串,而后执行 self.cout += 1操作
④ 当执行 self.cout 加 1 操作时,将会去寻找 count 这个属性,然而,由于此时 __init__尚未完成,并不存在 count这个属性,因此导致 'AttributeError' 错误
那么该如何更改呢?可以这样的:
class Test: def __init__(self): self.count = 0 def __settattr__(self, name, value): print('__setattr__') super().__settattr__(name, value+1) t = Test() t.count #__setattr__ #1

浙公网安备 33010602011771号