什么是描述符

python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 get, set, 和 __delete__。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。

属性: __dict__

作用:字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value}

对象属性的访问顺序:实例属性 → 类属性 → 父类属性 → getattr()方法

class Test(object):
    cls_val = 1
    def __init__(self):
        self.ins_val = 10

>>> t=Test()
>>> Test.__dict__
mappingproxy({'__module__': '__main__', 'cls_val': 1, '__init__': <function Test.__init__ at 0x0000000002E35D08>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None})
>>> t.__dict__
{'ins_val': 10}

>>> type(t)==Test
True

#更改实例t的属性cls_val,只是新增了该属性,并不影响类Test的属性cls_val
>>> t.cls_val = 20
>>> t.__dict__
{'ins_val': 10, 'cls_val': 20}
>>> Test.__dict__
mappingproxy({'__module__': '__main__', 'cls_val': 1, '__init__': <function Test.__init__ at 0x0000000002E35D08>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None})

#更改了类Test的属性cls_val的值,由于事先增加了实例t的cls_val属性,因此不会改变实例的cls_val值(井水不犯河水)
>>> Test.cls_val = 30
>>> t.__dict__
{'ins_val': 10, 'cls_val': 20}
>>> Test.__dict__
mappingproxy({'__module__': '__main__', 'cls_val': 30, '__init__': <function Test.__init__ at 0x0000000002E35D08>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}) 

从以上代码可以看出,实例t的属性并不包含cls_val,cls_val是属于类Test的。

魔术方法: __get__, __set__, __delete__

他们的原型分别为:

class Desc(object):
    
    def __get__(self, instance, owner):
        print("__get__...")
        print("self : \\t\\t", self)
        print("instance : \\t", instance)
        print("owner : \\t", owner)
        print('='*40, "\\n")
        
    def __set__(self, instance, value):
        print('__set__...')
        print("self : \\t\\t", self)
        print("instance : \\t", instance)
        print("value : \\t", value)
        print('='*40, "\\n")

class TestDesc(object):
    x = Desc()

#以下为测试代码
t = TestDesc()
t.x

#以下为输出信息:
# __get__...
# self :          <__main__.Desc object at 0x0000000002B0B828>
# instance :      <__main__.TestDesc object at 0x0000000002B0BA20>
# owner :      <class '__main__.TestDesc'> 

可以看到,实例化类TestDesc后,调用对象t访问其属性x,会自动调用类Desc的 __get__方法,由输出信息可以看出:

在这里, Desc类就是是一个描述符(描述符是一个类哦),为啥呢?因为类Desc定义了方法 get, set.