zoukankan      html  css  js  c++  java
  • python __getattr__ 巧妙应用

      在之前的文章有提到__getattr__函数的作用: 如果属性查找(attribute lookup)在实例以及对应的类中(通过__dict__)失败, 那么会调用到类的__getattr__函数, 如果没有定义这个函数,那么抛出AttributeError异常。由此可见,__getattr__一定是作用于属性查找的最后一步,兜底。
    我们来看几个例子:
     
    第一个例子,很简单但经典,可以像访问属性一样访问dict中的键值对。
      
     1 class ObjectDict(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         if isinstance(value, dict):
     8             value = ObjectDict(value)
     9         return value
    10 
    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 class WidgetShowLazyLoad(object):
     2     def fetch_complex_attr(self, attrname):
     3         '''可能是比较耗时的操作, 比如从文件读取'''
     4         return attrname
     5 
     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_attr
    15     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')  # 332833500
    第三个例子,我觉的是最实用的,__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:
  • 相关阅读:
    [纯C#实现]基于BP神经网络的中文手写识别算法
    【转载】Jedis对管道、事务以及Watch的操作详细解析
    redis 缓存用户账单策略
    redis 通配符 批量删除key
    explain分析sql效率
    mysql 常用命令大全
    【转载】实战mysql分区(PARTITION)
    mysql表名忽略大小写配置
    【转】微服务架构的分布式事务解决方案
    【转载】Mysql中的Btree与Hash索引比较
  • 原文地址:https://www.cnblogs.com/xybaby/p/6280313.html
Copyright © 2011-2022 走看看