定義區別:
_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異常