描述符的应用
__get__
,__getattr__
和__getattribute__
都是访问属性的方法,但不太相同。
object.__getattribute__(self, name)
无条件被调用,通过实例访问属性。如果class中定义了__getattr__()
,则__getattr__()
不会被调用(除非显示调用或引发AttributeError异常)
object.__getattr__(self, name)
当一般位置找不到__attribute__
的时候,会调用__getattr__
,返回一个值或AttributeError异常。
描述符
如果一个对象或者类中定义了__get__
,__set__
,__delete__
其中一个或者几个就称为描述符。
一句话概括:描述符就是可重用的属性
在这里我要告诉你:从根本上讲,描述符就是可以重复使用的属性。也就是说,描述符可以让你编写这样的代码:
f = Foo()
b = f.bar # bar就是一个描述符
f.bar = c
del f.bar
而在解释器执行上述代码时,当发现你试图访问属性(b = f.bar)、对属性赋值(f.bar = c)或者删除一个实例变量的属性(del f.bar)时,就会去调用自定义的方法。
描述符的应用场所
- 框架,例如django的ORM
- classmethod,staticmethod,property的实现
- 缓存,Flask的作者写了一个werkzeug网络工具库中的cached_property
object.__get__(self, instance, owner)
owner是所有者的类,instance是访问descriptor的实例,如果不是通过实例访问,而是通过类访问的话,instance则为None。(descriptor的实例自己访问自己是不会触发__get__
,只有descriptor作为其它类的属性才有意义。)(所以下文的d是作为C2的一个属性被调用)
classC(object):
a='abc'
def __getattribute__(self,*args,**kwargs):
print("__getattribute__() is called")
return object.__getattribute__(self,*args,**kwargs)
# return "haha"
def __getattr__(self, name):
print("__getattr__() is called ")
return name +" from getattr"
def __get__(self, instance, owner):
print("__get__() is called", instance, owner)
return self
def foo(self, x):
print(x)
classC2(object):
d=C()
if __name__ =='__main__':
c=C()
c2=C2()
print(c.a)
print(c.zzzzzzzz)
c2.d
print(c2.d.a)
输出结果:
__getattribute__() is called
abc
__getattribute__() is called
__getattr__() is called
zzzzzzzz from getattr
__get__() is called <__main__.C2 objectat 0x16d2310> <class'__main__.C2'>
__get__() is called <__main__.C2 objectat 0x16d2310> <class'__main__.C2'>
__getattribute__() is called
abc