详解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 strip()函数 介绍
May 24 Python
Python多线程编程(七):使用Condition实现复杂同步
Apr 05 Python
Python实现感知机(PLA)算法
Dec 20 Python
Python3导入自定义模块的三种方法详解
Apr 13 Python
Python文件监听工具pyinotify与watchdog实例
Oct 15 Python
使用Python做定时任务及时了解互联网动态
May 15 Python
用Python批量把文件复制到另一个文件夹的实现方法
Aug 16 Python
在Python3 numpy中mean和average的区别详解
Aug 24 Python
python使用socket实现的传输demo示例【基于TCP协议】
Sep 24 Python
解决pyCharm中 module 调用失败的问题
Feb 12 Python
Python图像处理库PIL的ImageDraw模块介绍详解
Feb 26 Python
Python 调用有道翻译接口实现翻译
Mar 02 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得到某段时间区间的时间戳 php定时任务
2012/04/12 PHP
php 文件上传实例代码
2012/04/19 PHP
解析linux下安装memcacheq(mcq)全过程笔记
2013/06/27 PHP
PHP+MySQL删除操作实例
2015/01/21 PHP
JavaScript中this关键字使用方法详解
2007/03/08 Javascript
JavaScript 动态添加表格行 使用模板、标记
2009/10/24 Javascript
javascript复制对象使用说明
2011/06/28 Javascript
jQuery Ajax 仿AjaxPro.Utility.RegisterTypeForAjax辅助方法
2011/09/27 Javascript
基于JQuery的列表拖动排序实现代码
2013/10/01 Javascript
Javascript之this关键字深入解析
2013/11/12 Javascript
使用javascript实现简单的选项卡切换
2015/01/09 Javascript
图解js图片轮播效果
2015/12/20 Javascript
NodeJS创建基础应用并应用模板引擎
2016/04/12 NodeJs
jQuery动态生成Bootstrap表格
2016/11/01 Javascript
利用transition实现文字上下抖动的效果
2017/01/21 Javascript
jQuery插件autocomplete使用详解
2017/02/04 Javascript
详解Vue中组件的缓存
2019/04/20 Javascript
jquery.pager.js分页实现详解
2019/07/29 jQuery
vue.js实现只能输入数字的输入框
2019/10/19 Javascript
使用PYTHON接收多播数据的代码
2012/03/01 Python
Python函数的周期性执行实现方法
2016/08/13 Python
python中如何使用正则表达式的非贪婪模式示例
2017/10/09 Python
浅谈Tensorflow由于版本问题出现的几种错误及解决方法
2018/06/13 Python
Python这样操作能存储100多万行的xlsx文件
2019/04/16 Python
python numpy 矩阵堆叠实例
2020/01/17 Python
Python itertools.product方法代码实例
2020/03/27 Python
英国最大的海报商店:GB Posters
2018/03/20 全球购物
中国一家专注拼团的社交购物网站:拼多多
2018/06/13 全球购物
内容编辑个人求职信
2013/12/10 职场文书
二年级体育教学反思
2014/01/15 职场文书
经典婚礼主持词
2014/03/13 职场文书
护士个人年度总结范文
2015/02/13 职场文书
民事撤诉申请书范本
2015/05/18 职场文书
python中sys模块的介绍与实例
2021/04/17 Python
Python与C++中梯度方向直方图的实现
2022/03/17 Python
「Manga Time Kirara MAX」2022年5月号封面公开
2022/03/21 日漫