详解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实现百度关键词排名查询
Mar 30 Python
详解Python中expandtabs()方法的使用
May 18 Python
python数据类型判断type与isinstance的区别实例解析
Oct 31 Python
详解python3中tkinter知识点
Jun 21 Python
Python OpenCV处理图像之图像像素点操作
Jul 10 Python
对pycharm 修改程序运行所需内存详解
Dec 03 Python
python交易记录整合交易类详解
Jul 03 Python
python Web flask 视图内容和模板实现代码
Aug 23 Python
Python tkinter布局与按钮间距设置方式
Mar 04 Python
Python3 ID3决策树判断申请贷款是否成功的实现代码
May 21 Python
Python使用configparser读取ini配置文件
May 25 Python
python中if和elif的区别介绍
Nov 07 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 iconv 函数转gb2312的bug解决方法
2009/10/11 PHP
PHP sprintf()函数用例解析
2011/05/18 PHP
PHP程序级守护进程的实现与优化的使用概述
2013/05/02 PHP
浅析Yii2 gridview实现批量删除教程
2016/04/22 PHP
php封装的mongodb操作类代码
2017/08/06 PHP
PHP常用日期加减计算方法实例小结
2018/07/31 PHP
laravel5实现微信第三方登录功能
2018/12/06 PHP
JavaScript高级程序设计 读书笔记之九 本地对象Array
2012/02/27 Javascript
javascript获取作用在元素上面的样式属性代码
2012/09/20 Javascript
jQuery简单实现网页选项卡特效
2014/11/24 Javascript
Redis基本知识、安装、部署、配置笔记
2015/03/05 Javascript
JavaScript运动减速效果实例分析
2015/08/04 Javascript
基于JS2Image实现圣诞树代码
2015/12/24 Javascript
BootStrap3学习笔记(一)之网格系统
2016/05/20 Javascript
Vue + Vue-router 同名路由切换数据不更新的方法
2017/11/20 Javascript
深入理解ES6之数据解构的用法
2018/01/13 Javascript
Vue2 轮播图slide组件实例代码
2018/05/31 Javascript
移动端手指操控左右滑动的菜单
2019/09/08 Javascript
[02:16]深扒TI7聊天轮盘语音出处2
2017/05/11 DOTA
通过python下载FTP上的文件夹的实现代码
2013/02/10 Python
Python实现的数据结构与算法之双端队列详解
2015/04/22 Python
Python向日志输出中添加上下文信息
2017/05/24 Python
Python如何读取文件中图片格式
2020/01/13 Python
python中np是做什么的
2020/07/21 Python
HTML5 Canvas 实现圆形进度条并显示数字百分比效果示例
2017/08/18 HTML / CSS
美国高端寝具品牌:Coyuchi
2017/02/08 全球购物
泰国演唱会订票网站:StubHub泰国
2018/02/26 全球购物
澳大利亚宠物食品和药物在线:Jumbo Pets
2018/03/24 全球购物
学生实习介绍信
2014/01/15 职场文书
学校大课间活动方案
2014/01/30 职场文书
《再见了,亲人》教学反思
2014/02/26 职场文书
合作经营协议书
2014/04/17 职场文书
2014年人事科工作总结
2014/11/19 职场文书
我的1919观后感
2015/06/03 职场文书
Go语言 详解net的tcp服务
2022/04/14 Golang
Debian11 Xfce终端光标的颜色怎么设置?
2022/08/14 数码科技