对比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 相关文章推荐
详解Django中Request对象的相关用法
Jul 17 Python
分享一个可以生成各种进制格式IP的小工具实例代码
Jul 28 Python
python实现图书馆研习室自动预约功能
Apr 27 Python
使用pandas批量处理矢量化字符串的实例讲解
Jul 10 Python
python保存二维数组到txt文件中的方法
Nov 15 Python
Python实现读取txt文件中的数据并绘制出图形操作示例
Feb 26 Python
python爬虫租房信息在地图上显示的方法
May 13 Python
Python turtle绘画象棋棋盘
Aug 21 Python
python获取依赖包和安装依赖包教程
Feb 13 Python
Python分类测试代码实例汇总
Jul 23 Python
python pymysql库的常用操作
Oct 16 Python
Python 统计序列中元素的出现频度
Apr 26 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与MySQL交互使用详解
2006/10/09 PHP
php 删除cookie方法详解
2014/12/01 PHP
mac os快速切换多个PHP版本的方法
2017/03/07 PHP
PHP信号处理机制的操作代码讲解
2019/04/19 PHP
不间断滚动JS打包类,基本可以实现所有的滚动效果,太强了
2007/12/08 Javascript
两个DIV等高的JS的实现代码
2007/12/23 Javascript
js常见表单应用技巧
2008/01/09 Javascript
技术男用来对妹子表白的百度首页
2014/07/23 Javascript
NodeJS连接MongoDB数据库时报错的快速解决方法
2016/05/13 NodeJs
node.js操作mongodb简单示例分享
2017/05/25 Javascript
Mac系统下Webstorm快捷键整理大全
2017/05/28 Javascript
基于require.js的使用(实例讲解)
2017/09/07 Javascript
vue组件父与子通信详解(一)
2017/11/07 Javascript
微信小程序使用request网络请求操作实例
2017/12/15 Javascript
浅谈VUE-CLI脚手架热更新太慢的原因和解决方法
2018/09/28 Javascript
详解vue中this.$emit()的返回值是什么
2019/04/07 Javascript
详解Vue底部导航栏组件
2019/05/02 Javascript
vue项目打包为APP,静态资源正常显示,但API请求不到数据的操作
2020/09/12 Javascript
vue脚手架项目创建步骤详解
2021/03/02 Vue.js
[01:58]最残酷竞争 2016国际邀请赛中国区预选赛积分循环赛回顾
2016/06/28 DOTA
[54:33]2018DOTA2亚洲邀请赛小组赛 A组加赛 Liquid vs Optic
2018/04/03 DOTA
探索Python3.4中新引入的asyncio模块
2015/04/08 Python
浅谈python中的变量默认是什么类型
2016/09/11 Python
Python开发的实用计算器完整实例
2017/05/10 Python
Python 判断 有向图 是否有环的实例讲解
2018/02/01 Python
python读写LMDB文件的方法
2018/07/02 Python
python使用__slots__让你的代码更加节省内存
2018/09/05 Python
详解Python 实现 ZeroMQ 的三种基本工作模式
2020/03/24 Python
python中matplotlib实现随鼠标滑动自动标注代码
2020/04/23 Python
Python如何截图保存的三种方法(小结)
2020/09/01 Python
好莱坞百老汇御用王牌美妆:Koh Gen Do 江原道
2018/04/03 全球购物
美国电力供应商店/电气批发商:USESI
2018/10/12 全球购物
巴西儿童时尚购物网站:Dinda
2019/08/14 全球购物
导游词之平津战役纪念馆
2019/11/04 职场文书
七年级作文之冬景
2019/11/07 职场文书
golang通过递归遍历生成树状结构的操作
2021/04/28 Golang