python __getattr__ & __getattribute__ 学习

实例属性的获取和拦截, 仅对实例属性(instance, variable)有效, 非类属性

getattr: 适用于未定义的属性, 即该属性在实例中以及对应的类的基类以及祖先类中都不存在
1. 动态处理事先未定义的属性, 可更好的实现数据隐藏, 当调用dir(obj)时只会显示初始化定义的正常的属性和方法
getattribute: 对于所有属性的访问都会调用该方法, 当属性不存在时会报错
1. 覆盖该方法之后,任何属性的访问都会调用用户自定义的__getattribute__()方法, 性能上会有所损耗.

  1. 当两个方法同步被重写, 要么在__getattribute__()中显示调用, 要么是触发AttributeError异常时, getattr()才会被调用
  2. 要避免无穷递归调用
  3. 如果访问未定义属性, 且在__getattr__中未跑出AttributeError异常或者显性的返回一个值, 则会返回None

参考: 编写高质量代码:改善Python程序的91个建议

class Attribute(object):
    def __init__(self, name):
        self.name = name

    def __getattribute__(self, key):
        try:
            print('calling __getattribute__.{key}'.format(key=key))
            # 调用超类
            return super(Attribute, self).__getattribute__(key)
            # return object.__getattribute__(self, key)
        except KeyError:
            return 'default'
        except AttributeError as ex:  # 捕获了该异常就不用调用__getattr__
            print(ex)

    def __getattr__(self, key):
        # 什么时候被调用:
        # 1.当属性不在实例以及基类和祖先类的__dict__
        # 2. 当触发AtrributeError异常时(不仅仅是__getattribute__()引发的 AttributeError),porperty中定义的get()方法也会抛出异常
        print('calling __getattr__.{key}'.format(key=key))
        # return 'default'

测试 property, getattribute, __getattr__调用顺序

class A(object):
    _c = 'test'
    def __init__(self):
        self.x = None

    @property
    def a(self):
        print('using property to access attribute')
        if self.x is None:
            print('return value')
            return 'a'
        else:
            print('error occured')
            raise AttributeError

    @a.setter
    def a(self, value):
        self.x = value

    def __getattribute__(self, name):
        print('using __getattribute__ to access attribute')
        return object.__getattribute__(self, name)

    def __getattr__(self, name):
        print('using __getattr__ to access attribute')
        print('attribute name: ', name)
        return 'b'



if __name__ == '__main__':
    a = Attribute('atest')
    print(a.name)
    print(a.test)

    # 测试 property, __getattribute__, __getattr__调用顺序
    # b = A()
    # print(b.a)
    # print('-'*50)
    # b.a = 10
    # print(b.a)
    # print('-'*50)
    # print(A._c)

defaultdict 和 魔法方法__missing__()

dict中并不存在__missing__()方法, 需要派生子类并重写__missing__()方法

# python 2.5 之前默认要自己重写__missing__方法
# python3以后 __missing__(key) called by __getitem__ for missing key;
class DefaultDict(dict):
    def __init__(self, default_factory, *args, **kwargs):
        super(DefaultDict, self).__init__(*args, **kwargs)    
        self.default_factory = default_factory
    
    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            return self.__missing__(key)    

    def __missing__(self, key):
        self[key] = value = self.default_factory()
        return value

posted @ 2019-11-14 15:08  JonPan  阅读(268)  评论(0编辑  收藏  举报