正文
__getattr__函數(shù)的作用: 如果屬性查找(attribute lookup)在實(shí)例以及對(duì)應(yīng)的類中(通過(guò)__dict__)失敗减噪, 那么會(huì)調(diào)用到類的__getattr__函數(shù), 如果沒(méi)有定義這個(gè)函數(shù)慎璧,那么拋出AttributeError異常朴肺。由此可見酒来,__getattr__一定是作用于屬性查找的最后一步,兜底床玻。
我們來(lái)看幾個(gè)例子:
第一個(gè)例子赏陵,很簡(jiǎn)單但經(jīng)典,可以像訪問(wèn)屬性一樣訪問(wèn)dict中的鍵值對(duì)买乃。
class ObjectDict(dict):
def __init__(self, *args, **kwargs):
super(ObjectDict, self).__init__(*args, **kwargs)
def __getattr__(self, name):
value = self[name]
if isinstance(value, dict):
value = ObjectDict(value)
return value
if __name__ == '__main__':
od = ObjectDict(asf={'a': 1}, d=True)
print od.asf
print od.asf.a
print od.d
第二個(gè)例子姻氨,對(duì)象屬性的lazy initialize。
class WidgetShowLazyLoad(object):
def fetch_complex_attr(self, attrname):
'''可能是比較耗時(shí)的操作剪验, 比如從文件讀取'''
return attrname
def __getattr__(self, name):
if name not in self.__dict__:
self.__dict__[name] = self.fetch_complex_attr(name)
return self.__dict__[name]
if __name__ == '__main__':
w = WidgetShowLazyLoad()
print 'before', w.__dict__
a=w.lazy_loaded_attr
print 'after', w.__dict__
可以看到肴焊,屬性訪問(wèn)前對(duì)象中的__dict__沒(méi)有任何元素,訪問(wèn)之后就有添加功戚。
這個(gè)例子是類實(shí)例的屬性的惰性初始化娶眷,bottle里面也有一個(gè)用descriptor實(shí)現(xiàn)類屬性的惰性初始化。
import functools
class lazy_attribute(object):
""" A property that caches itself to the class object. """
def __init__(self, func):
functools.update_wrapper(self, func, updated=[])
self.getter = func
def __get__(self, obj, cls):
value = self.getter(cls)
setattr(cls, self.__name__, value)
return value
class Widget(object):
@lazy_attribute
def complex_attr_may_not_need(clz):
print 'complex_attr_may_not_need is needed now'
return sum(i*i for i in range(1000))
if __name__ == '__main__':
print Widget.__dict__.get('complex_attr_may_not_need')
Widget.complex_attr_may_not_need
print Widget.__dict__.get('complex_attr_may_not_need')
第三個(gè)例子啸臀,我覺(jué)的是最實(shí)用的届宠,__getattr__使得實(shí)現(xiàn)adapter wrapper模式非常容易,我們都知道“組合優(yōu)于繼承”乘粒,__getattr__實(shí)現(xiàn)的adapter就是以組合的形式豌注。
class adaptee(object):
def foo(self):
print 'foo in adaptee'
def bar(self):
print 'bar in adaptee'
class adapter(object):
def __init__(self):
self.adaptee = adaptee()
def foo(self):
print 'foo in adapter'
self.adaptee.foo()
def __getattr__(self, name):
print 'name',name
return getattr(self.adaptee, name)
if __name__ == '__main__':
a = adapter()
a.foo()
a.bar()
如果adapter需要修改adaptee的行為,那么定義一個(gè)同名的屬性就行了谓厘,其他的想直接“繼承”的屬性幌羞,通通交給__getattr__就行了
最后一個(gè)例子,實(shí)際用到__getattr__竟稳,本質(zhì)上和第三個(gè)例子差不多
class AlgoImpA(object):
def __init__(self):
self.obj_attr = 'obj_attr in AlgoImpA'
def foo(self):
print 'foo in AlgoImpA'
def bar(self):
print 'bar in AlgoImpA'
class AlgoImpB(object):
def __init__(self):
self.obj_attr = 'obj_attr in AlgoImpB'
def foo(self):
print 'foo in AlgoImpB'
def bar(self):
print 'bar in AlgoImpB'
class Algo(object):
def __init__(self):
self.imp_a = AlgoImpA()
self.imp_b = AlgoImpB()
self.cur_imp = self.imp_a
def switch_imp(self):
if self.cur_imp == self.imp_a:
self.cur_imp = self.imp_b
else:
self.cur_imp = self.imp_a
def __str__(self):
return 'Algo with imp %s' % str(self.cur_imp)
def __getattr__(self, name):
return getattr(self.cur_imp, name)
if __name__ == '__main__':
algo = Algo()
print algo
print algo.obj_attr
algo.foo()
algo.switch_imp()
print algo
print algo.obj_attr
algo.bar()