详解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 17 Python
深入讨论Python函数的参数的默认值所引发的问题的原因
Mar 30 Python
简单解决Python文件中文编码问题
Nov 22 Python
python+pyqt实现右下角弹出框
Oct 26 Python
Python实现的选择排序算法原理与用法实例分析
Nov 22 Python
python多线程实现TCP服务端
Sep 03 Python
利用python Selenium实现自动登陆京东签到领金币功能
Oct 31 Python
python实现大战外星人小游戏实例代码
Dec 26 Python
如何在django中实现分页功能
Apr 22 Python
详解tensorflow2.x版本无法调用gpu的一种解决方法
May 25 Python
python能在浏览器能运行吗
Jun 17 Python
python日志通过不同的等级打印不同的颜色(示例代码)
Jan 13 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
层叠菜单的动态生成
2006/10/09 PHP
php接口与接口引用的深入解析
2013/08/09 PHP
javascript 使td内容不换行不撑开
2012/11/29 Javascript
jQuery中is()方法用法实例
2015/01/06 Javascript
基于JavaScript实现生成名片、链接等二维码
2015/09/20 Javascript
深入学习JavaScript的AngularJS框架中指令的使用方法
2016/03/05 Javascript
实用又漂亮的BootstrapValidator表单验证插件
2016/05/30 Javascript
CSS3 3D 技术手把手教你玩转
2016/09/02 Javascript
原生JS实现垂直手风琴效果
2017/02/19 Javascript
webpack配置sass模块的加载的方法
2017/07/30 Javascript
JavaScript中Object值合并方法详解
2017/12/22 Javascript
在vue项目中安装使用Mint-UI的方法
2017/12/27 Javascript
详解处理Vue单页面应用SEO的另一种思路
2018/11/09 Javascript
el-table树形表格表单验证(列表生成序号)
2020/05/31 Javascript
[45:46]2014 DOTA2国际邀请赛中国区预选赛5.21 HGT VS DT
2014/05/23 DOTA
python删除列表中重复记录的方法
2015/04/28 Python
python计算圆周率pi的方法
2015/07/11 Python
Python中字典(dict)合并的四种方法总结
2017/08/10 Python
Python 3.6 读取并操作文件内容的实例
2018/04/23 Python
Python用5行代码写一个自定义简单二维码
2018/10/21 Python
学习python的前途 python挣钱
2019/02/27 Python
Python 中Django验证码功能的实现代码
2019/06/20 Python
如何运行.ipynb文件的图文讲解
2019/06/27 Python
python使用 cx_Oracle 模块进行查询操作示例
2019/11/28 Python
Python 如何实现访问者模式
2020/07/28 Python
基于Python正确读取资源文件
2020/09/14 Python
HTML5 新旧语法标记对我们有什么好处
2012/12/13 HTML / CSS
详解canvas绘制多张图的排列顺序问题
2019/01/21 HTML / CSS
极度干燥澳大利亚官方网站:Superdry澳大利亚
2019/03/28 全球购物
欧洲著名的二手奢侈品网站:Vestiaire Collective
2020/03/07 全球购物
职工运动会感言
2014/02/07 职场文书
售后服务经理岗位职责
2014/02/25 职场文书
激励口号大全
2014/06/17 职场文书
活动总结新闻稿
2014/08/30 职场文书
基于Python实现射击小游戏的制作
2022/04/06 Python
Python中time标准库的使用教程
2022/04/13 Python