详解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 27 Python
SVM基本概念及Python实现代码
Dec 27 Python
Python中的defaultdict与__missing__()使用介绍
Feb 03 Python
python2.7读取文件夹下所有文件名称及内容的方法
Feb 24 Python
Python-OpenCV基本操作方法详解
Apr 02 Python
Django的性能优化实现解析
Jul 30 Python
使用tqdm显示Python代码执行进度功能
Dec 08 Python
pytorch对梯度进行可视化进行梯度检查教程
Feb 04 Python
python实现批量转换图片为黑白
Jun 16 Python
python使用XPath解析数据爬取起点小说网数据
Apr 22 Python
python 标准库原理与用法详解之os.path篇
Oct 24 Python
Pyhton爬虫知识之正则表达式详解
Apr 01 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中在PDO中使用事务(Transaction)
2011/05/14 PHP
PHP中使用gettext解决国际化问题的例子(i18n)
2014/06/13 PHP
php中filter_input函数用法分析
2014/11/15 PHP
兼容IE与firefox火狐的回车事件(js与jquery)
2010/10/20 Javascript
JavaScript实现输入框(密码框)出现提示语
2016/01/12 Javascript
jQuery+正则+文本框只能输入数字的实现方法
2016/10/07 Javascript
微信小程序 数据绑定详解及实例
2016/10/25 Javascript
jQuery.datatables.js插件用法及api实例详解
2016/10/28 Javascript
EasyUI折叠表格层次显示detailview详解及实例
2016/12/28 Javascript
canvas 绘制圆形时钟
2017/02/22 Javascript
mongoose设置unique不生效问题的解决及如何移除unique的限制
2017/11/07 Javascript
分享5个好用的javascript文件上传插件
2018/09/16 Javascript
angular6开发steps步骤条组件
2019/07/04 Javascript
bootstrap+spring boot实现面包屑导航功能(前端代码)
2019/10/09 Javascript
解决在Vue中使用axios用form表单出现的问题
2019/10/30 Javascript
Vuex的实战使用详解
2019/10/31 Javascript
鸿蒙系统中的 JS 开发框架
2020/09/18 Javascript
对于Python中线程问题的简单讲解
2015/04/03 Python
在Python的Flask框架中实现全文搜索功能
2015/04/20 Python
在Python的web框架中配置app的教程
2015/04/30 Python
详解python单例模式与metaclass
2016/01/15 Python
Python的Scrapy爬虫框架简单学习笔记
2016/01/20 Python
利用python实现命令行有道词典的方法示例
2017/01/31 Python
深入理解Python分布式爬虫原理
2017/11/23 Python
对python自动生成接口测试的示例讲解
2018/11/30 Python
python实现合并两个排序的链表
2019/03/03 Python
如何清空python的变量
2020/07/05 Python
中国跨境在线时尚零售商:Bellelily
2018/04/06 全球购物
财务简历的自我评价
2014/03/05 职场文书
个人委托书
2014/07/31 职场文书
心得体会的写法
2014/09/05 职场文书
孔庙导游词
2015/02/04 职场文书
2015年财政局工作总结
2015/05/21 职场文书
红与黑读书笔记
2015/06/29 职场文书
高考要来啦!用Python爬取历年高考数据并分析
2021/06/03 Python
Java 超详细讲解十大排序算法面试无忧
2022/04/08 Java/Android