在之前的文章有提到__getattr__函数的作用: 如果属性查找(attribute lookup)在实例以及对应的类中(通过__dict__)失败, 那么会调用到类的__getattr__函数, 如果没有定义这个函数,那么抛出AttributeError异常。由此可见,__getattr__一定是作用于属性查找的最后一步,兜底。
我们来看几个例子:
 
第一个例子,很简单但经典,可以像访问属性一样访问dict中的键值对。
  

 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, od.asf.a # {'a': 1} 1
print od.d # True
第二个例子,对象属性的lazy initialize。
   

 class WidgetShowLazyLoad(object):
def fetch_complex_attr(self, attrname):
'''可能是比较耗时的操作, 比如从文件读取'''
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__
w.lazy_loaded_attr
print 'after', w.__dict__
输出:
    before {}
    after {'lazy_loaded_attr': 'lazy_loaded_attr'}
 
可以看到,属性访问前对象中的__dict__没有任何元素,访问之后就有添加。
这个例子是类实例的属性的惰性初始化,bottle里面也有一个用descriptor实现类属性的惰性初始化。
  

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') # <__main__.lazy_attribute object at 0x02B12450>
Widget.complex_attr_may_not_need # complex_attr_may_not_need is needed now
print Widget.__dict__.get('complex_attr_may_not_need') #
第三个例子,我觉的是最实用的,__getattr__使得实现adapter wrapper模式非常容易,我们都知道“组合优于继承”,__getattr__实现的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):
return getattr(self.adaptee, name) if __name__ == '__main__':
a = adapter()
a.foo()
a.bar()
如果adapter需要修改adaptee的行为,那么定义一个同名的属性就行了,其他的想直接“继承”的属性,通通交给__getattr__就行了
 
最后一个例子,是笔者在工作中实际用到__getattr__的例子。本质上和第三个例子差不多
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()
 输出:

Algo with imp <__main__.AlgoImpA object at 0x02AA2270>
obj_attr in AlgoImpA
foo in AlgoImpA
Algo with imp <__main__.AlgoImpB object at 0x02AA22B0>
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:

python __getattr__ 巧妙应用的更多相关文章

  1. python __getattr__ & __getattribute__ 学习

    实例属性的获取和拦截, 仅对实例属性(instance, variable)有效, 非类属性 getattr: 适用于未定义的属性, 即该属性在实例中以及对应的类的基类以及祖先类中都不存在 1. 动态 ...

  2. Python - __getattr__和__getattribute__的区别

    传送门 https://docs.python.org/3/reference/datamodel.html#object.__getattr__ https://docs.python.org/3/ ...

  3. 某校高中生利用Python,巧妙获取考试成绩,看到成绩后无言以对!

    Python是非常有吸引力的编程语言,学习Python的不是帅哥就是美女.为什么这么说呢?因为我和我的女朋友都是学习Python认识的,小编肯定是帅哥,不用去怀疑,而且我眼光特高. 给大伙讲一个故事, ...

  4. python __getattr__

    1.__getattr__ 方法的作用:当调用不存在的属性,就会调用__getattr__()方法: 当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeEr ...

  5. python __getattr__ __setattr__

    class Rectangle: def __init__(self): self.width = 0 self.height = 0 def __setattr__(self, key, value ...

  6. python __getattr__和 __getattribute__

    __getattr__ 这个魔法函数会在类中查找不到属性时调用 class User: def __init__(self): self.info = 1 def __getattr__(self, ...

  7. Python中__get__, __getattr__, __getattribute__的区别及延迟初始化

    本节知识点 1.__get__, __getattr__, __getattribute__的区别 2.__getattr__巧妙应用 3.延迟初始化(lazy property) 1.__get__ ...

  8. Python数据结构与算法设计总结篇

    1.Python数据结构篇 数据结构篇主要是阅读[Problem Solving with Python]( http://interactivepython.org/courselib/static ...

  9. [leetcode]Permutations @ Python

    原题地址:https://oj.leetcode.com/problems/permutations/ 题意: Given a collection of numbers, return all po ...

随机推荐

  1. 约瑟夫环C#解决方法

    /*约瑟夫环 (问题描述) 约瑟夫问题的一种描述是:编号为1,2,......n,的n个人按顺时针方向围坐一圈,每个人持有一个密码(正整数).一开始任意选 一个正整数作为报数的上限值m,从第一个人开始 ...

  2. 一个ICON图标的转换程序

    抽空写了一个ICON图标的转换程序,支持png\jpe\bmp格式到ico的转换.具体的程序就在下面,如果看的人多,过两天再把思路写一下.废话不说,见代码.                       ...

  3. c#关键字及ref和out

    最近在写程序时遇到ref,out 参数问题.回头有自习看了看MSDN,才有巩固了基础.我把我的测试程序贴出来,大家分享一下.    ref 关键字使参数按引用传递.其效果是,当控制权传递回调用方法时, ...

  4. web前端-----JAVA Script(一)

      JavaScript概述 JavaScript的历史 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中).后将其改名ScriptEase ...

  5. ZedBoard开发板学习记录(一)之开发环境的搭建(Ubuntu16.04)以及运行HelloWorld程序的测试

    ZedBoard开发板由PL和PS两大部分组成, 对PS操作,一般有两个办法: (1).在Windows系统上面,使用SDK新建C Project SDK自带编译环境,编译后自动产生elf文件.使用U ...

  6. Python argparse模块实现模拟 linux 的ls命令

    python 模拟linux的 ls 命令 sample: python custom_ls.py -alh c:/ 选项: -a ,--all 显示所有文件,包括'.'开头的隐藏文件 -l  列表显 ...

  7. Python数据分析(二): Pandas技巧 (2)

    Pandas的第一部分: http://www.cnblogs.com/cgzl/p/7681974.html github地址: https://github.com/solenovex/My-Ma ...

  8. jquery总结(来自于一个讲师的总结)

    选择器 基本选择器:id class 标签 eq()查找具体的列表中的元素:$('ul li:eq(n)').eq(n) 层 :div p,div>p 查找:find 选中元素中再查找子元素,p ...

  9. Regasm

      程序集注册工具(Regasm.exe) 读取程序集中的元数据,并将所需的项添加到注册表中.注册表允许COM 客户程序以透明方式创建.NET Framework类.类一经注册,任何COM 客户程序都 ...

  10. 2715:谁拿了最多奖学金-poj

    总时间限制:  1000ms 内存限制:  65536kB 描述 某校的惯例是在每学期的期末考试之后发放奖学金.发放的奖学金共有五种,获取的条件各自不同: 1) 院士奖学金,每人8000元,期末平均成 ...