本来以为自己对__getattribute__已经比较了解了,结果用到的时候,才发现有一些知识点之前一直没有真正弄明白,记录如下(针对python3,python2差异较大):

  1. object类有__getattribute__属性,因此所有的类默认就有__getattribute__属性(所有类都继承自object),object的__getattribute__起什么用呢?它做的就是查找自定义类的属性,如果属性存在则返回属性的值,如果不存在则抛出AttributeError。

    还是看代码:
>>> class A:
def __getattribute__(self, name):
print('in A')
return super().__getattribute__(name) >>> class B(A):
def spam(self):
print('spam') >>> b = B()
>>> in A
b.spam()
in A
spam

当在命令行输入b.spam(,甚至括号还没有输入完整的时候,就已经触发了__getattribute__。可见,当实例在查找任何属性的时候(注意是实例,这是另一个知识点,后面再详谈),都会触发__getattribute__方法。

基于此特性,可以方便的使用类装饰器为其所有属性(不管是有的还是没有的)扩充功能,例如,加一个log记录功能,代码如下:

>>> def log_attr(cls):
cls_getattribute = cls.__getattribute__
def new_getattribute(self, name):
print('catch name')
return cls_getattribute(self, name)
cls.__getattribute__ = new_getattribute
return cls >>> @log_attr
class C:
def __init__(self, x):
self.x = x
def spam(self):
print('spam') >>> c = C(42)
>>> c.x
catch name
42
>>> catch name
c.spam()
catch name
spam
>>> c.y
catch name
Traceback (most recent call last):
File "<pyshell#83>", line 1, in <module>
c.y
File "<pyshell#77>", line 5, in new_getattribute
return cls_getattribute(self, name)
AttributeError: 'C' object has no attribute 'y'
  1. 只有在实例的命名空间查找属性的时候,才会触发__getattribute__,在类的命名空间中查找,是不会触发__getattribute__的。

    还是第一个例子:
>>> B.spam
<function B.spam at 0x000001D3759646A8>
>>> b = B()
>>> B.spam(b)
spam

可见,当类直接调用spam方法的时候,不会触发__getattribute__,而且当以B.spam(b)形式调用的时候,巧妙的绕过了__getattribute__。

接下来,是一个重要的知识点,即特殊的方法,如__len__,__str__等等操作符重载方法,当隐式调用的时候,在python3中会直接在类的命名空间中查找,因此是不会触发__getattribute__的!!如下:

>>> class C:
def __len__(self):
return 42
def __getattribute__(self, name):
print(f'catch {name}')
return super().__getattribute__(name) >>> c = C()
>>> len(c)
42

but,当直接显示调用的时候,python不会将其当作啥特殊方法,仍然会从实例的命名空间查找,此时,就会触发__getattribute__:

>>> c.__len__()
catch __len__
42

最大的影响就是,在委托类型的代码中,操作符重载的方法代码得重新写,如下:

>>> class A:
def __len__(self):
return 42
def attr1(self):
print('attr1') >>> class B:
def __init__(self):
self.a = A()
def __getattribute__(self, name):
if name == 'a':
return super().__getattribute__(name)
else:
return getattr(self.a, name) >>> b.attr1()
attr1
>>> len(b)
Traceback (most recent call last):
File "<pyshell#170>", line 1, in <module>
len(b)
TypeError: object of type 'B' has no len()

可见,attr1正确的被代理,但是len方法没有被代理,因为隐式调用的时候,直接在B类的命名空间查找__len__方法,根本就没有触发__getattribute__。如果显示调用,就可以被代理了。

>>> b.__len__()
42

最后,还有一个坑需要注意,如上例B类的__getattribute__方法中,判断一下a是否B自身的实例属性的代码不可少,是则调用父类的__getattribute__方法返回正确的self.a,否则在getattr(self.a, name)中,self.a会不断的触发__getattribute__,从而会陷入无限循环。

对了,最后还有一小点,有一个比较特殊的操作符重载方法,即dir,因为它会从实例的命名空间开始查找__dict__和__class__特殊方法,因此也会触发__getattribute__。

貌似关于__getattribute__的知识点就这些了,以上均为个人原创,平时学习的积累,打字不易,转载还请注明出处。

最新文章

  1. 文法分类的python实现
  2. 【转】Android开发之如何保证Service不被杀掉(broadcast+system/app)
  3. Robot Framework--01 创建简单工程示例
  4. mac os x安装php7.0和phalcon3.0
  5. ios cell左滑删除
  6. 自定义view 画圆
  7. Activtiy
  8. hdu 1078 FatMouse and Cheese 记忆化dp
  9. JS提取URL中的参数
  10. [转]centos7环境安装rabbitMQ
  11. ArrayList在foreach正常迭代删除不报错的原因
  12. Mybatis 笔记
  13. 【记录一次坑经历】axios使用x-www-form-urlencoded 服务器报400(错误的请求。 )(后端.Net MVC5 WebApi OAuth,前端Electron-Vue)
  14. 软件目录结构规范(以python为例)
  15. linux服务器开发三(网络编程)
  16. msgpack和protobuf的对比
  17. HDUOJ-------2844Coins
  18. 一个轻量级分布式 RPC 框架 — NettyRpc
  19. 伪多项式时间 Pseudo-polynomial time
  20. Sqoop数据迁移工具

热门文章

  1. iOS判断字母、数字串
  2. SpringBoot30 整合Mybatis-Plus、整合Redis、利用Ehcache实现二级缓存、利用SpringCache和Redis作为缓存
  3. Io 异常: The Network Adapter could not establish the connection解决方案
  4. Hamming Distance二进制距离
  5. cactiez中文版10.1配置监控系统安装笔记
  6. jedis的publish/subscribe[转]含有redis源码解析
  7. C++编程语言学习资料
  8. [C++] Type Conversion(类型转换)
  9. zigbee组播通信原理
  10. CodeForces 682B Alyona and Mex (题意水题)