详解Python中 __get__和__getattr__和__getattribute__的区别


Posted in Python onJune 16, 2016

引子
假设我们有个类A,其中a是A的实例
a.x时发生了什么?属性的lookup顺序如下:

  • 如果重载了__getattribute__,则调用.
  • a.__dict__, 实例中是不允许有descriptor的,所以不会遇到descriptor
  • A.__dict__, 也即a.__class__.__dict__ .如果遇到了descriptor,优先调用descriptor.
  • 沿着继承链搜索父类.搜索a.__class__.__bases__中的所有__dict__. 如果有多重继承且是菱形继承的情况,按MRO(Method Resolution Order)顺序搜索.

如果以上都搜不到,则抛AttributeError异常.

ps.从上面可以看到,dot(.)操作是昂贵的,很多的隐式调用,特别注重性能的话,在高频的循环内,可以考虑绑定给一个临时局部变量.

class C(object): 
  def __setattr__(self, name, value): 
    print "__setattr__ called:", name, value 
    object.__setattr__(self, name, value) 
 
  def __getattr__(self, name): 
    print "__getattr__ called:", name 
 
  def __getattribute__(self, name): 
    print "__getattribute__ called:",name 
    return object.__getattribute__(self, name) 
 
c = C() 
c.x = "foo" 
print c.__dict__['x'] 
print c.x


深入
1.object.__getattr__(self, name)
当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeError异常。

2.object.__getattribute__(self, name)
无条件被调用,通过实例访问属性。如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常)

3.object.__get__(self, instance, owner)
如果class定义了它,则这个class就可以称为descriptor。owner是所有者的类,instance是访问descriptor的实例,如果不是通过实例访问,而是通过类访问的话,instance则为None。(descriptor的实例自己访问自己是不会触发__get__,而会触发__call__,只有descriptor作为其它类的属性才有意义。)(所以下文的d是作为C2的一个属性被调用)

class C(object): 
  a = 'abc' 
  def __getattribute__(self, *args, **kwargs): 
    print("__getattribute__() is called") 
    return object.__getattribute__(self, *args, **kwargs) 
#    return "haha" 
  def __getattr__(self, name): 
    print("__getattr__() is called ") 
    return name + " from getattr" 
   
  def __get__(self, instance, owner): 
    print("__get__() is called", instance, owner) 
    return self 
   
  def foo(self, x): 
    print(x) 
 
class C2(object): 
  d = C() 
if __name__ == '__main__': 
  c = C() 
  c2 = C2() 
  print(c.a) 
  print(c.zzzzzzzz) 
  c2.d 
  print(c2.d.a)

输出结果是:

__getattribute__() is called 
abc 
__getattribute__() is called 
__getattr__() is called  
zzzzzzzz from getattr 
__get__() is called <__main__.C2 object at 0x16d2310> <class '__main__.C2'> 
__get__() is called <__main__.C2 object at 0x16d2310> <class '__main__.C2'> 
__getattribute__() is called 
abc

小结:

可以看出,每次通过实例访问属性,都会经过__getattribute__函数。而当属性不存在时,仍然需要访问__getattribute__,不过接着要访问__getattr__。这就好像是一个异常处理函数。
每次访问descriptor(即实现了__get__的类),都会先经过__get__函数。

需要注意的是,当使用类访问不存在的变量是,不会经过__getattr__函数。而descriptor不存在此问题,只是把instance标识为none而已。

Python 相关文章推荐
简单谈谈python中的多进程
Nov 06 Python
python实现windows下文件备份脚本
May 27 Python
详解Python字符串切片
May 20 Python
Python流行ORM框架sqlalchemy安装与使用教程
Jun 04 Python
django框架基于queryset和双下划线的跨表查询操作详解
Dec 11 Python
基于torch.where和布尔索引的速度比较
Jan 02 Python
python识别验证码图片实例详解
Feb 17 Python
sklearn+python:线性回归案例
Feb 24 Python
浅析python表达式4+0.5值的数据类型
Feb 26 Python
30行Python代码实现高分辨率图像导航的方法
May 22 Python
Python如何实现后端自定义认证并实现多条件登陆
Jun 22 Python
Pycharm编辑器功能之代码折叠效果的实现代码
Oct 15 Python
Python利用带权重随机数解决抽奖和游戏爆装备问题
Jun 16 #Python
Python黑魔法@property装饰器的使用技巧解析
Jun 16 #Python
Python实现类似jQuery使用中的链式调用的示例
Jun 16 #Python
浅析Python中else语句块的使用技巧
Jun 16 #Python
python基础教程之分支、循环简单用法
Jun 16 #Python
python3音乐播放器简单实现代码
Apr 20 #Python
使用python3.5仿微软记事本notepad
Jun 15 #Python
You might like
PHP 面向对象实现代码
2009/11/11 PHP
PHP排序之二维数组的按照字母排序实现代码
2011/08/13 PHP
实现PHP多线程异步请求的3种方法
2014/01/17 PHP
php无限遍历文件夹示例分享
2014/03/04 PHP
php sybase_fetch_array使用方法
2014/04/15 PHP
smarty实现多级分类的方法
2014/12/05 PHP
在Laravel中实现使用AJAX动态刷新部分页面
2019/10/15 PHP
用jQuery简化JavaScript开发分析
2009/02/19 Javascript
Prototype PeriodicalExecuter对象 学习
2009/07/19 Javascript
通过百度地图获取公交线路的站点坐标的js代码
2012/05/11 Javascript
jquery实现的一个文章自定义分段显示功能
2014/05/23 Javascript
new Date()问题在ie8下面的处理方法
2014/07/31 Javascript
JavaScript原型及原型链终极详解
2016/01/04 Javascript
Webpack 实现 AngularJS 的延迟加载
2016/03/02 Javascript
JQuery 在文档中查找指定name的元素并移除的实现方法
2016/05/19 Javascript
js获取当前年月日-YYYYmmDD格式的实现代码
2016/06/01 Javascript
深入理解JavaScript中的块级作用域、私有变量与模块模式
2016/10/31 Javascript
jquery做个日期选择适用于手机端示例
2017/01/10 Javascript
bootstrap的工具提示实例代码
2017/05/17 Javascript
nodejs使用express获取get和post传值及session验证的方法
2017/11/09 NodeJs
使用layui+ajax实现简单的菜单权限管理及排序的方法
2019/09/10 Javascript
vue中activated的用法
2021/01/03 Vue.js
Python 统计字数的思路详解
2018/05/08 Python
Dlib+OpenCV深度学习人脸识别的方法示例
2019/05/14 Python
基于django传递数据到后端的例子
2019/08/16 Python
Python函数的默认参数设计示例详解
2019/12/01 Python
python实现opencv+scoket网络实时图传
2020/03/20 Python
Python系统公网私网流量监控实现流程
2020/11/23 Python
Too Faced官网:美国知名彩妆品牌
2017/03/07 全球购物
Skyscanner阿联酋:全球领先的旅游搜索平台
2017/11/25 全球购物
教师自荐信范文
2013/12/09 职场文书
本科生职业生涯规划书范文
2014/01/21 职场文书
《苏珊的帽子》教学反思
2014/04/07 职场文书
法人授权委托书范本
2014/09/17 职场文书
2015最新婚礼司仪主持词
2015/06/30 职场文书
2016年师德师风学习心得体会
2016/01/12 职场文书