详解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判断、获取一张图片主色调的2个实例
Apr 10 Python
解析Python中的变量、引用、拷贝和作用域的问题
Apr 07 Python
Python捕捉和模拟鼠标事件的方法
Jun 03 Python
Linux上安装Python的PIL和Pillow库处理图片的实例教程
Jun 23 Python
基于python中staticmethod和classmethod的区别(详解)
Oct 24 Python
python list转矩阵的实例讲解
Aug 04 Python
Python 使用Numpy对矩阵进行转置的方法
Jan 28 Python
处理python中多线程与多进程中的数据共享问题
Jul 28 Python
python yield和Generator函数用法详解
Feb 10 Python
pytorch masked_fill报错的解决
Feb 18 Python
Django中日期时间型字段进行年月日时分秒分组统计
Nov 27 Python
K近邻法(KNN)相关知识总结以及如何用python实现
Jan 28 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通过baihui网API实现读取word文档并展示
2015/06/22 PHP
thinkPHP实现递归循环栏目并按照树形结构无限极输出的方法
2016/05/19 PHP
php版阿里大于(阿里大鱼)短信发送实例详解
2016/11/30 PHP
如何离线执行php任务
2017/02/21 PHP
js左侧多级菜单动态的解决方案
2010/02/01 Javascript
javascript ready和load事件的区别示例介绍
2013/08/30 Javascript
javascript ajax 仿百度分页函数
2013/10/29 Javascript
关于删除时的提示处理(确定删除吗)
2013/11/03 Javascript
ExtJS的拖拽效果示例
2013/12/09 Javascript
jquery easyui中treegrid用法的简单实例
2014/02/18 Javascript
javascript 处理null及null值示例
2014/06/09 Javascript
javascript适合移动端的日期时间拾取器
2015/11/10 Javascript
轻松学习jQuery插件EasyUI EasyUI实现树形网络基本操作(2)
2015/11/30 Javascript
基于JS实现省市联动效果代码分享
2016/06/06 Javascript
使用Fullpage插件快速开发整屏翻页的页面
2017/09/13 Javascript
vue.js使用3DES加密的方法示例
2018/05/18 Javascript
Vue引用Swiper4插件无法重写分页器样式的解决方法
2018/09/27 Javascript
Jquery的autocomplete插件用法及参数讲解
2019/03/12 jQuery
javascript实现贪吃蛇游戏(娱乐版)
2020/08/17 Javascript
利用vue3+ts实现管理后台(增删改查)
2020/10/30 Javascript
[01:06:32]DOTA2上海特级锦标赛D组资格赛#1 EG VS VP第一局
2016/02/28 DOTA
Python中的lstrip()方法使用简介
2015/05/19 Python
Python爬虫抓取代理IP并检验可用性的实例
2018/05/07 Python
解决Python pandas plot输出图形中显示中文乱码问题
2018/12/12 Python
wxPython电子表格功能wx.grid实例教程
2019/11/19 Python
使用python实现微信小程序自动签到功能
2020/04/27 Python
如何使用python切换hosts文件
2020/04/29 Python
python连接手机自动搜集蚂蚁森林能量的实现代码
2021/02/24 Python
Clearly新西兰:购买眼镜、太阳镜和隐形眼镜
2018/04/26 全球购物
英国在线发型和美容产品商店:Beauty Cutie
2019/04/27 全球购物
六一儿童节活动策划方案
2014/01/27 职场文书
厂区绿化方案
2014/05/08 职场文书
电大奖学金获奖感言
2014/08/14 职场文书
小区门卫岗位职责范本
2014/08/24 职场文书
3.15消费者权益日活动总结
2015/02/09 职场文书
JUnit5常用注解的使用
2021/07/02 Java/Android