描述符

描述符的应用

  • 缓存
  • Django中ORM的QuerySet

__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> 
__get__() is called <__main__.C2 objectat 0x16d2310> 
__getattribute__() is called
abc

参考:

流程的python
Python 黑魔法 --- 描述器
解密 Python 的描述符

你可能感兴趣的:(描述符)