python getattr_python __getattr__ 巧妙应用

在之前的文章有提到__getattr__函数的作用: 如果属性查找(attribute lookup)在实例以及对应的类中(通过__dict__)失败, 那么会调用到类的__getattr__函数, 如果没有定义这个函数,那么抛出AttributeError异常。由此可见,__getattr__一定是作用于属性查找的最后一步,兜底。

我们来看几个例子:

第一个例子,很简单但经典,可以像访问属性一样访问dict中的键值对。

1 classObjectDict(dict):2 def __init__(self, *args, **kwargs):3 super(ObjectDict, self).__init__(*args, **kwargs)4

5 def __getattr__(self, name):6 value =self[name]7 ifisinstance(value, dict):8 value =ObjectDict(value)9 returnvalue10

11 if __name__ == '__main__':12 od = ObjectDict(asf={'a': 1}, d=True)13 print od.asf, od.asf.a #{'a': 1} 1

14 print od.d #True

第二个例子,对象属性的lazy initialize。

1 classWidgetShowLazyLoad(object):2 deffetch_complex_attr(self, attrname):3 '''可能是比较耗时的操作, 比如从文件读取'''

4 returnattrname5

6 def __getattr__(self, name):7 if name not in self.__dict__:8 self.__dict__[name] =self.fetch_complex_attr(name)9 return self.__dict__[name]10

11 if __name__ == '__main__':12 w =WidgetShowLazyLoad()13 print 'before', w.__dict__

14 w.lazy_loaded_attr15 print 'after', w.__dict__

输出:

before {}

after {'lazy_loaded_attr': 'lazy_loaded_attr'}

可以看到,属性访问前对象中的__dict__没有任何元素,访问之后就有添加。

这个例子是类实例的属性的惰性初始化,bottle里面也有一个用descriptor实现类属性的惰性初始化。

importfunctoolsclasslazy_attribute(object):"""A property that caches itself to the class object."""

def __init__(self, func):

functools.update_wrapper(self, func, updated=[])

self.getter=funcdef __get__(self, obj, cls):

value=self.getter(cls)

setattr(cls, self.__name__, value)returnvalueclassWidget(object):

@lazy_attributedefcomplex_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') #<__main__.lazy_attribute object at>

Widget.complex_attr_may_not_need #complex_attr_may_not_need is needed now

print Widget.__dict__.get('complex_attr_may_not_need') #332833500

第三个例子,我觉的是最实用的,__getattr__使得实现adapter wrapper模式非常容易,我们都知道“组合优于继承”,__getattr__实现的adapter就是以组合的形式。

classadaptee(object):deffoo(self):print 'foo in adaptee'

defbar(self):print 'bar in adaptee'

classadapter(object):def __init__(self):

self.adaptee=adaptee()deffoo(self):print 'foo in adapter'self.adaptee.foo()def __getattr__(self, name):returngetattr(self.adaptee, name)if __name__ == '__main__':

a=adapter()

a.foo()

a.bar()

如果adapter需要修改adaptee的行为,那么定义一个同名的属性就行了,其他的想直接“继承”的属性,通通交给__getattr__就行了

最后一个例子,是笔者在工作中实际用到__getattr__的例子。本质上和第三个例子差不多

classAlgoImpA(object):def __init__(self):

self.obj_attr= 'obj_attr in AlgoImpA'

deffoo(self):print 'foo in AlgoImpA'

defbar(self):print 'bar in AlgoImpA'

classAlgoImpB(object):def __init__(self):

self.obj_attr= 'obj_attr in AlgoImpB'

deffoo(self):print 'foo in AlgoImpB'

defbar(self):print 'bar in AlgoImpB'

classAlgo(object):def __init__(self):

self.imp_a=AlgoImpA()

self.imp_b=AlgoImpB()

self.cur_imp=self.imp_adefswitch_imp(self):if self.cur_imp ==self.imp_a:

self.cur_imp=self.imp_belse:

self.cur_imp=self.imp_adef __str__(self):return 'Algo with imp %s' %str(self.cur_imp)def __getattr__(self, name):returngetattr(self.cur_imp, name)if __name__ == '__main__':

algo=Algo()printalgoprintalgo.obj_attr

algo.foo()

algo.switch_imp()printalgoprintalgo.obj_attr

algo.bar()

输出:

Algo with imp <__main__.algoimpa object at>

obj_attr in AlgoImpA

foo in AlgoImpA

Algo with imp <__main__.algoimpb object at>

obj_attr in AlgoImpB

bar in AlgoImpB

首先,Algo提供给使用者的接口应该尽量简单,因此应该使用algo.func, 而不是algo.cur_imp.func。其次,AlgoImpA和AlgoImpB都有很多的属性(泛指函数和数据属性),使用__getattr__能大幅简化代码。Why we use python,life is short。

references:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值