Python的__getattribute__ vs __getattr__的妙用
这里的属性即包括属性变量,也包括属性方法。即类的变量和方法。
当访问某个实例属性时, getattribute会被无条件调用,如未实现自己的getattr方法,会抛出
AttributeError
提示找不到这个属性,如果自定义了自己getattr方法的话,方法会在这种找不到属性的情况下被调用,比如上面的例子中的情况。所以在找不到属性的情况下通过实现自定义的getattr方法来实现一些功能是一个不错的方式,因为它不会像getattribute方法每次都会调用可能会影响一些正常情况下的属性访问 使用这几个方法可以实现拦截器啥、动态代理、统一log等功能。
举例:
1、使用__getattribute__实现统一的打印日志功能。使用__getattribute__方法拦截了属性和方法的访问。__getattribute__只有在新式类中才能使用。
# -*- coding: utf-8 -*- class Fjs(object): def __init__(self, name): self.name = name def hello(self): print "said by : ", self.name def __getattribute__(self, item): print "访问了特性:" + item return object.__getattribute__(self, item) fjs = Fjs("fjs") print fjs.name fjs.hello()
输出:
访问了特性:name
fjs
访问了特性:hello
said by : 访问了特性:name
fjs
2、这里通过__getattr__方法,将所有的特性的访问都路由给了内部的fjs对象
# -*- coding: utf-8 -*- class Fjs(object): def __init__(self, name): self.name = name def hello(self): print "said by : ", self.name def fjs(self, name): if name == self.name: print "yes" else: print "no" class Wrap_Fjs(object): def __init__(self, fjs): self._fjs = fjs def __getattr__(self, item): if item == "hello": print "调用hello方法了" elif item == "fjs": print "调用fjs方法了" return getattr(self._fjs, item) fjs = Wrap_Fjs(Fjs("fjs")) fjs.hello() fjs.fjs("fjs")
输出:
调用hello方法了
said by : fjs
调用fjs方法了
yes
3、使用类的继承实现。则不会路由,子类直接继承了父类的属性和方法
# -*- coding: utf-8 -*- class Fjs(object): def __init__(self, name): self.name = name def hello(self): print "said by : ", self.name def fjs(self, name): if name == self.name: print "yes" else: print "no" class Wrap_Fjs(Fjs): def __init__(self, fjs): self._fjs = fjs def __getattr__(self, item): if item == "hello": print "调用hello方法了" elif item == "fjs": print "调用fjs方法了" return getattr(self._fjs, item) fjs = Wrap_Fjs(Fjs("fjs")) fjs.hello() fjs.fjs("fjs")
输出:
said by : fjs
yes
4、猜一下结果,理解其妙用
# 例子在原来的基础上简化了一下,排除依赖和干扰,详细参见原项目 class UrlGenerator(object): def __init__(self, root_url): self.url = root_url def __getattr__(self, item): if item == 'get' or item == 'post': print self.url return UrlGenerator('{}/{}'.format(self.url, item)) url_gen = UrlGenerator('http://xxxx') url_gen.users.show.get
5、通过转换,可以像访问属性一样访问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
参考:
1、https://www.jianshu.com/p/885d59db57fc
2、https://www.cnblogs.com/xybaby/p/6280313.html