在之前的文章有提到__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#编写的艺术字类方法

    代码如下: using System;using System.Collections.Generic;using System.ComponentModel;using System.Drawing ...

  2. ANDROID基础ACTIVITY篇之Activity的加载模式

    在这之前首先让我们先了解一下什么是Task Task,简单的说,就是一组以栈的模式聚集在一起的Activity组件集合.它们有潜在的前后驱关联,新加入的Activity组件,位于栈顶,并仅有在栈顶的A ...

  3. 【转】Entity Framework 5.0系列之约定配置

    Code First之所以能够让开发人员以一种更加高效.灵活的方式进行数据操作有一个重要的原因在于它的约定配置.现在软件开发越来复杂,大家也都试图将软件设计的越来越灵活,很多内容我们都希望是可配置的, ...

  4. 运行第一个 Service - 每天5分钟玩转 Docker 容器技术(96)

    上一节我们创建好了 Swarm 集群, 现在部署一个运行 httpd 镜像的 service,执行如下命令: docker service create --name web_server httpd ...

  5. 安装VisualSVN Server时候,端口号冲突

    今天在本机安装VisualSVN Server 时,发现https默认端口号:443被占用了, 于是到cmd下面执行 netstat -ano命令发现是pid:4276的进程在试用, 打开任务管理里一 ...

  6. bootstrap --> reset css

    /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) */ html, ...

  7. AES加密实现

    起因 这段时间因为要对接一个外部接口,其参数的加密方式为AES,就需要学下AES的加密写法,但网上的资料不是很全,自己记录下遇到的坑: 基本写法 String str = "hello&qu ...

  8. java虚拟机概述

    java 虚拟机是什么?       java虚拟机是一个将字节码指令映射为对应物理操作系统指令的程序.       java程序的运行需要事先安装 jdk,而在jdk内部的jre中其核心就是 jvm ...

  9. yii2.0中添加二维数组,多条数据。

    /** * @inheritdoc 批量添加 * @params $add array 添加数据 */public function add_all($add){ $connection = \Yii ...

  10. Swift 线程安全数组

    欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者:BigNerdCoding 有并发的地方就存在线程安全问题,尤其是对于 Swift 这种还没有内置并发支持的语言来说线程安全问题更为突出 ...