对比Python中__getattr__和 __getattribute__获取属性的用法


Posted in Python onJune 21, 2016

相信大家觉得大多数时候我们并不太需要关注getattribute和getattr的一些细节(至少我自己吧:)),
一般情况下消费我们自定义的类的时候,我们对类的结构都了解,不会刻意偏离,造成一些属性访问的错误。

不过作为一个有好奇心有追求有气质的python宝宝,怎么可能不稍稍研究一下呢。好吧,其实是在github上读到一个开源项目sinaweibopy的源码才看的,代码挺有意思,正好当作一个实用的例子,来看看如何自定义实现gettattr让代码更加的动态优雅:

# 例子在原来的基础上简化了一下,排除依赖和干扰,详细参见原项目
class UrlGenerator(object):
  def __init__(self, root_url):
    self.url = root_url

  def __getattr__(self, item):
    if item == 'get' or item == 'post':
      print self.url
    return UrlGenerator('{}/{}'.format(self.url, item))


url_gen = UrlGenerator('http://xxxx')
url_gen.users.show.get

>>> http://xxxx/users/show

充分利用getattr会在没有查找到相应实例属性时被调用的特点,方便的通过链式调用生成对应的url,源代码中在碰到http method的时候返回一个
可调用的对象更加的优雅,链式的操作不仅优雅而且还能很好的说明调用的接口的意义(restful的接口啦)。

示例
1.__getattr__示例:

class Test(object):
  def __init__(self,name):
    self.name = name
  def __getattr__(self, value):
    if value == 'address':
      return 'China'

if __name__=="__main__":
  test = Test('letian')
  print test.name
  print test.address
  test.address = 'Anhui'
  print test.address

运行结果:

letian
China
Anhui

如果是调用了一个类中未定义的方法,则__getattr__也要返回一个方法,例如:

class Test(object):
  def __init__(self,name):
    self.name = name
  def __getattr__(self, value):
    return len

if __name__=="__main__":
  test = Test('letian')
  print test.getlength('letian')

运行结果:
6

2.__getattribute__示例:

class Test(object):
  def __init__(self,name):
    self.name = name
  def __getattribute__(self, value):
    if value == 'address':
      return 'China'
    

if __name__=="__main__":
  test = Test('letian')
  print test.name
  print test.address
  test.address = 'Anhui'
  print test.address

运行结果:

None
China
China

深入思考
既然能通过定制类的getattr自定义方法来实现一些优雅的功能,自然我们也要对它有一些了解,包括和它相似的自定义方法getattribute

1. 用作实例属性的获取和拦截
当访问某个实例属性时, getattribute会被无条件调用,如未实现自己的getattr方法,会抛出AttributeError提示找不到这个属性,如果自定义了自己getattr方法的话,方法会在这种找不到属性的情况下被调用,比如上面的例子中的情况。所以在找不到属性的情况下通过实现自定义的getattr方法来实现一些功能是一个不错的方式,因为它不会像getattribute方法每次都会调用可能会影响一些正常情况下的属性访问:

class Test(object):
  def __init__(self, p):
    self.p = p

  def __getattr__(self, item):
    return 'default'

t = Test('p1')
print t.p
print t.p2

>>> p1
>>> default

2. 自定义getattribute的时候防止无限递归
因为getattribute在访问属性的时候一直会被调用,自定义的getattribute方法里面同时需要返回相应的属性,通过self.__dict__取值会继续向下调用getattribute,造成循环调用:

class AboutAttr(object):
  def __init__(self, name):
    self.name = name

  def __getattribute__(self, item):
    try:
      return super(AboutAttr, self).__getattribute__(item)
    except KeyError:
      return 'default'

这里通过调用绑定的super对象来获取队形的属性,对新式类来说其实和object.__getattribute__(self, item)一样的道理:

默认情况下自定义的类会从object继承getattribute方法,对于属性的查找是完全能用的
getattribute的实现感觉还是挺抽象化的,只需要绑定相应的实例对象和要查找的属性名称就行
3.同时覆盖掉getattribute和getattr的时候,在getattribute中需要模仿原本的行为抛出AttributeError或者手动调用getattr

class AboutAttr(object):
  def __init__(self, name):
    self.name = name

  def __getattribute__(self, item):
    try:
      return super(AboutAttr, self).__getattribute__(item)
    except KeyError:
      return 'default'
    except AttributeError as ex:
      print ex

  def __getattr__(self, item):
    return 'default'

at = AboutAttr('test')
print at.name
print at.not_exised

>>>test
>>>'AboutAttr' object has no attribute 'not_exised'
>>>None

上面例子里面的getattr方法根本不会被调用,因为原本的AttributeError被我们自行处理并未抛出,也没有手动调用getattr,所以访问not_existed的结果是None而不是default.

Python 相关文章推荐
Phantomjs抓取渲染JS后的网页(Python代码)
May 13 Python
python+django加载静态网页模板解析
Dec 12 Python
深入浅析Python获取对象信息的函数type()、isinstance()、dir()
Sep 17 Python
Python3 获取一大段文本之间两个关键字之间的内容方法
Oct 11 Python
python中将两组数据放在一起按照某一固定顺序shuffle的实例
Jul 15 Python
对Django 中request.get和request.post的区别详解
Aug 12 Python
Python爬虫图片懒加载技术 selenium和PhantomJS解析
Sep 18 Python
python模块hashlib(加密服务)知识点讲解
Nov 25 Python
六种酷炫Python运行进度条效果的实现代码
Jul 17 Python
Python常用类型转换实现代码实例
Jul 28 Python
在pycharm中文件取消用 pytest模式打开的操作
Sep 01 Python
 python中的元类metaclass详情
May 30 Python
常见python正则用法的简单实例
Jun 21 #Python
小议Python中自定义函数的可变参数的使用及注意点
Jun 21 #Python
简单讲解Python编程中namedtuple类的用法
Jun 21 #Python
Python编程中实现迭代器的一些技巧小结
Jun 21 #Python
Centos Python2 升级到Python3的简单实现
Jun 21 #Python
Python的Django框架中forms表单类的使用方法详解
Jun 21 #Python
Python正则表达式使用经典实例
Jun 21 #Python
You might like
PHP 第一节 php简介
2012/04/28 PHP
php结合ajax实现赞、顶、踩功能实例
2014/05/12 PHP
php生成随机数的三种方法
2014/09/10 PHP
thinkphp模板继承实例简述
2014/11/26 PHP
PHP中字符安全过滤函数使用小结
2015/02/25 PHP
PHP实现支付宝即时到账功能
2016/12/21 PHP
js 浮动层菜单收藏
2009/01/16 Javascript
jQuery 事件队列调整方法
2009/09/18 Javascript
jQuery中与toggleClass等价的程序段 以及未来学习的方向
2010/03/18 Javascript
js 居中漂浮广告
2010/03/21 Javascript
JQuery1.6 使用方法三
2011/11/23 Javascript
载入jQuery库的最佳方法详细说明及实现代码
2012/12/28 Javascript
jquery的ajaxSubmit()异步上传图片并保存表单数据演示代码
2013/06/04 Javascript
详解JavaScript中Date.UTC()方法的使用
2015/06/12 Javascript
原生JS实现平滑回到顶部组件
2016/03/16 Javascript
jQuery实现最简单实用的分秒倒计时
2017/02/05 Javascript
简单谈谈原生js的math对象
2017/06/27 Javascript
SeaJS中use函数用法实例分析
2017/10/10 Javascript
[01:21:07]EG vs Liquid 2018国际邀请赛淘汰赛BO3 第一场 8.25
2018/08/29 DOTA
[00:06]Yes,it worked!小卡尔成功穿越时空加入战场!
2019/07/20 DOTA
Python中的一些陷阱与技巧小结
2015/07/10 Python
使用python为mysql实现restful接口
2018/01/05 Python
详解Python图像处理库Pillow常用使用方法
2019/09/02 Python
python输出pdf文档的实例
2020/02/13 Python
python torch.utils.data.DataLoader使用方法
2020/04/02 Python
Python如何定义有可选参数的元类
2020/07/31 Python
html5 canvas合成海报所遇问题及解决方案总结
2017/08/03 HTML / CSS
乌克兰珠宝大卖场:Zlato.ua
2020/09/27 全球购物
.net C#面试题
2012/08/28 面试题
电大学习个人自我评价范文
2013/10/04 职场文书
幼儿园实习生辞职信
2014/01/20 职场文书
绩效工资实施方案
2014/03/15 职场文书
小学校园广播稿(3篇)
2014/09/19 职场文书
优秀共产党员推荐材料
2014/12/18 职场文书
Golang全局变量加锁的问题解决
2021/05/08 Golang
python-for x in range的用法(注意要点、细节)
2021/05/10 Python