定义区别:
_getattr_(self, item)
获取实例
的属性时,仅当实例属性中不包括item时,才被调用。这个方法应该返回相应的属性值或者抛出 AttributeError 异常_getattribute_(self, item)
获取实例
属性时,无条件调用该方法,不论实例属性中是否包括item
应用实例
- 利用
__getattr__
结合递归实现url动态生成器,代码来自于github-sinaweibopy
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
- 自己的django项目中,结合
__getattribute__
与decorator实现动态获取属性。
myconf = MyConfClient()
def myconf_decorator(key):
"""
首先从myconf中获取属性key,取不到再到origin_class取
"""
def wraper(origin_class):
# 定义被装饰类origin_class的__getattribute__方法
def __getattribute__(self, item):
try:
# 首先从myconf中获取属性
return myconf.get_dict(key)[item]
except (CanNotGetConfError, KeyError):
try:
# myconf中不存在属性key时,再从origin_class中查找
return self.item
except KeyError:
raise CanNotGetConfError('key: %s item: %s' % (key, item))
origin_class.__getattribute__ = __getattribute__
return origin_class()
return wraper
在写这个装饰器的过程中,出现了无限递归的情况!!
RecursionError: maximum recursion depth exceeded while calling a Python object
_getattribute_无限递归
__getattribute__
: 是无条件被调用。对任何对象的属性访问时,都会隐式的调用__getattribute__
方法,比如调用t.__dict__
,其实执行了t.__getattribute__("__dict__")
方法.
例如存在类A,a是A的一个实例,当我们通过a.x
获取属性值时,实际的过程:
- 如果类A中重载了
__getattribute__
,则调用__getattribute
- 没有重载
__getattribute__
,则调用a.\__dict__[x]
- 调用
A.\__dict__[x]
或者A.\__base__.__dict[x]
所以按照上面的写法,我们在a.x时,调用__getattribute__
方法,而该方法中使用self.key
,相当于再次调用__getattribute__
,造成无限递归。或者我们在重载__getattribute__
中又调用__dict__
的话,也会无限递归。
so,解决方法,在__getatribute__
方法中,调用object.__getattribute__
,切断无限递归:
def __getattribute__(self, item):
try:
# 首先从myconf中获取属性
return myconf.get_dict(key)[item]
except (CanNotGetConfError, KeyError):
try:
# myconf中不存在属性key时,再从origin_class中查找
return super(origin_class, self).__getattribute__(item)
except (KeyError, AttributeError):
raise CanNotGetConfError('key: %s item: %s' % (key, item))
而且如果没有重载__getattr__
方法时,__getattribute__
方法中需要处理AttributeError异常