详解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 生成目录树及显示文件大小的代码
Jul 23 Python
python读写文件操作示例程序
Dec 02 Python
Python set集合类型操作总结
Nov 07 Python
在Python中使用成员运算符的示例
May 13 Python
Python cookbook(字符串与文本)针对任意多的分隔符拆分字符串操作示例
Apr 19 Python
django 多数据库配置教程
May 30 Python
Python爬虫 urllib2的使用方法详解
Sep 23 Python
Python从列表推导到zip()函数的5种技巧总结
Oct 23 Python
pygame实现俄罗斯方块游戏(AI篇2)
Oct 29 Python
Matplotlib中%matplotlib inline如何使用
Jul 28 Python
UI自动化定位常用实现方法代码示例
Oct 27 Python
教你如何使用Python下载B站视频的详细教程
Apr 29 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中使用CURL获取页面title例子
2015/01/07 PHP
javascript高亮效果的二种实现方法
2008/09/14 Javascript
JS中==与===操作符的比较
2009/03/21 Javascript
如何确保JavaScript的执行顺序 之jQuery.html深度分析
2011/03/03 Javascript
javascript页面加载完执行事件代码
2014/02/11 Javascript
JavaScript观察者模式(经典)
2015/12/09 Javascript
jQuery增加与删除table列的方法
2016/03/01 Javascript
基于jquery实现简单的分页控件
2016/03/17 Javascript
AngularJS Bootstrap详细介绍及实例代码
2016/07/28 Javascript
js判断价格,必须为数字且不能为负数的实现方法
2016/10/07 Javascript
学习Node.js模块机制
2016/10/17 Javascript
NPM 安装cordova时警告:npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to
2016/12/20 Javascript
基于Bootstrap分页的实例讲解(必看篇)
2017/07/04 Javascript
浅谈angular2 组件的生命周期钩子
2017/08/12 Javascript
js将当前时间格式化为 年-月-日 时:分:秒的实现代码
2018/01/20 Javascript
vue.js指令v-for使用以及下标索引的获取
2019/01/31 Javascript
解决Vue.js应用回退或刷新界面时提示用户保存修改问题
2019/11/24 Javascript
vue项目,代码提交至码云,iconfont的用法说明
2020/07/30 Javascript
[07:09]2014DOTA2国际邀请赛-Newbee再次发威成功晋级决赛
2014/07/19 DOTA
Python+matplotlib实现计算两个信号的交叉谱密度实例
2018/01/08 Python
Python多线程扫描端口代码示例
2018/02/09 Python
Python 修改列表中的元素方法
2018/06/26 Python
利用Python对文件夹下图片数据进行批量改名的代码实例
2019/02/21 Python
python matplotlib 画dataframe的时间序列图实例
2019/11/20 Python
如何基于python对接钉钉并获取access_token
2020/04/21 Python
win10下python3.8的PIL库安装过程
2020/06/08 Python
用CSS3绘制三角形的简单方法
2015/07/17 HTML / CSS
草莓网化妆品澳大利亚站:Strawberrynet AU
2017/12/18 全球购物
英国婴儿产品专家:Samuel Johnston
2020/04/20 全球购物
一道SQL存储过程面试题
2016/10/07 面试题
个人充满哲理的自我评价
2014/02/20 职场文书
听课评语大全
2014/04/30 职场文书
美术学专业求职信
2014/07/23 职场文书
2015年教师学期工作总结
2015/04/30 职场文书
pytorch常用数据类型所占字节数对照表一览
2021/05/17 Python
Django rest framework如何自定义用户表
2021/06/09 Python