详解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轻松查到删除自己的微信好友
Jan 10 Python
Python中的if、else、elif语句用法简明讲解
Mar 11 Python
Python3解决棋盘覆盖问题的方法示例
Dec 07 Python
python实现二叉树的遍历
Dec 11 Python
Python元组及文件核心对象类型详解
Feb 11 Python
python发送告警邮件脚本
Sep 17 Python
对python操作kafka写入json数据的简单demo分享
Dec 27 Python
浅谈Python大神都是这样处理XML文件的
May 31 Python
Python3中configparser模块读写ini文件并解析配置的用法详解
Feb 18 Python
Python爬虫获取op.gg英雄联盟英雄对位胜率的源码
Jan 29 Python
Django中的DateTimeField和DateField实现
Feb 24 Python
django如何自定义manage.py管理命令
Apr 27 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
为什么那些咖啡爱好者大多看不上连锁咖啡店?
2021/03/06 咖啡文化
发布一个迷你php+AJAX聊天程序[聊天室]提供下载
2007/07/21 PHP
php抓取https的内容的代码
2010/04/06 PHP
详解php的魔术方法__get()和__set()使用介绍
2012/09/19 PHP
使用php测试硬盘写入速度示例
2014/01/27 PHP
ThinkPHP之用户注册登录留言完整实例
2014/07/22 PHP
PHP PDOStatement::nextRowset讲解
2019/02/01 PHP
thinkphp5框架调用其它控制器方法 实现自定义跳转界面功能示例
2019/07/03 PHP
兼容FireFox 的 js 日历 支持时间的获取
2009/03/04 Javascript
颜色选择器 Color Picker,IE,Firefox,Opera,Safar
2010/11/25 Javascript
js String对象中常用方法小结(字符串操作)
2012/01/27 Javascript
LABjs、RequireJS、SeaJS的区别
2014/03/04 Javascript
jQuery扁平化风格下拉框美化插件FancySelect使用指南
2015/02/10 Javascript
Angular 路由route实例代码
2016/07/12 Javascript
javascript 中null和undefined区分和比较
2017/04/19 Javascript
react-native fetch的具体使用方法
2017/11/01 Javascript
Python cookbook(数据结构与算法)找到最大或最小的N个元素实现方法示例
2018/02/13 Python
pip install python 快速安装模块的教程图解
2019/10/08 Python
使用 Python 处理3万多条数据只要几秒钟
2020/01/19 Python
python画图常规设置方式
2020/03/05 Python
详解Python中pyautogui库的最全使用方法
2020/04/01 Python
Django框架配置mysql数据库实现过程
2020/04/22 Python
Python爬虫自动化爬取b站实时弹幕实例方法
2021/01/26 Python
露营世界:Camping World
2017/02/02 全球购物
廉价连衣裙和婚纱礼服在线销售:Tbdress
2019/02/28 全球购物
美体小铺印度官网:The Body Shop印度
2019/10/17 全球购物
纺织工程专业个人求职信范文
2014/01/27 职场文书
学生喝酒检讨书
2014/02/06 职场文书
美术毕业生求职信
2014/02/25 职场文书
商超业务员岗位职责
2014/03/12 职场文书
购房协议书
2014/04/11 职场文书
地理科学专业自荐信
2014/09/01 职场文书
离婚协议书范本(2014版)
2014/09/28 职场文书
大学生党员批评与自我批评范文
2014/10/14 职场文书
2015年学校教务处工作总结
2015/05/11 职场文书
2016年主题党日活动总结
2016/04/05 职场文书