33.描述器(二)

 

 

(三十四)描述器二

 

4:数据描述器

 

    # 示例1:
    class XKD1:
        def __init__(self):
            self.course = 'Python'
            print('XKD1.__init__')
        def __get__(self, instance, owner):
            # 这里的self为XKD1的实例. instance为实例, 如果是类访问,那么instance为None. owner是调用者的类
            print('self={} instance={} owner={}'.format(self, instance, owner))
            # todo: 返回XKD1的实例self
            return self
    class XKD2:
        x = XKD1()
        def __init__(self):
            print('XKD2.__init__')
            self.y = XKD1()   # 没有调用__get__方法
    print('*'*100)
    print(XKD2.x.course) # self=<__main__.XKD1 object at 0x108c39b38> instance=None owner=<class '__main__.XKD2'>
    print('*'*100)
    obj = XKD2()
    print('*'*100)
    print(obj.y)   # 返回 <__main__.XKD1 object at 0x103b56cf8>
    小结: 当非数据描述器是实例的变量时,实例访问非数据描述器不会调用__get__方法,只是访问了描述器类的实例
    # 示例2:数据描述器
    class XKD1:
        def __init__(self):
            self.course = 'Python'
            print('XKD1.__init__')
        def __get__(self, instance, owner):
            print('self={} instance={} owner={}'.format(self, instance, owner))
            return self
        def __set__(self, instance, value):
            print('self={} instance={} value={}'.format(self, instance, value))
            self.course = value
    class XKD2:
        x = XKD1()
        def __init__(self):
            print('XKD2.__init__')
            self.y = XKD1()   # 调用了__get__方法
    print('*'*100)
    print(XKD2.x.course)
    print('*'*100)
    obj = XKD2()
    print('*'*100)
    print(obj.x)
    小结: 当数据描述器是实例的变量时,实例访问数据描述器会调用描述器的__get__方法

 

5:非数据描述器和数据描述器的访问顺序

 

    # 示例1:非数据描述器
    class XKD1:
        def __init__(self):
            self.course = 'Python'
            print('XKD1.__init__')
        def __get__(self, instance, owner):
            print('self={} instance={} owner={}'.format(self, instance, owner))
            return self
    class XKD2:
        x = XKD1()
        def __init__(self):
            print('XKD2.__init__')
            self.x = 'XKD1'          # 实例访问的是自己的__dict__中的x属性
    print('*'*100)
    print(XKD2.x.course)
    print('*'*100)
    obj = XKD2()
    print('*'*100)
    print(obj.x)  # 返回 'XKD1'
    # 示例2:数据描述器
    class XKD1:
        def __init__(self):
            self.course = 'Python'
            print('XKD1.__init__')
        def __get__(self, instance, owner):
            print('self={} instance={} owner={}'.format(self, instance, owner))
            return self
        def __set__(self, instance, value):
            print('self={} instance={} value={}'.format(self, instance, value))
            self.course = value
    class XKD2:
        x = XKD1()
        def __init__(self):
            print('XKD2.__init__')
            self.x = 'XKD1'          # 实例这里访问的是类属性x,也就是数据描述器,会调用数据描述器的__get__方法
    print('*'*100)
    print(XKD2.x.course)
    print('*'*100)
    obj = XKD2()
    print('*'*100)
    print(obj.x)  # 调用__get__方法,返回 XKD1实例
    小结: 一个类实例的查找属性顺序为:先查找类或父类中的数据描述器属性, 在查找自己__dict__中的属性,再查找类或父类的非数据描述器, 数据描述器优先于实例的__dict__, 实例的__dict__属性优先于非数据描述器, 当存在描述器的时候,属性的查找顺序为:类或父类的数据描述器属性 --> 实例的__dict__属性 --> 类或父类的非数据描述器属性

 

 

 

4.数据描述器

  • 当非数据描述器是实例的变量时,实例访问非数据描述器不会调用__get__方法,只是访问了描述器类的实例;

  • 当数据描述器是实例的变量时,实例访问数据描述器会调用描述器的__get__方法;

5.非数据描述器和数据描述器的访问顺序

当存在描述器的时候,一个类实例的查找属性顺序为:先查找类或父类中是否有数据描述器属性,如果有那么,先访问数据描述器,如果没有数据描述器 --> 那么就会查找自己实例的__dict__属性,如果__dict__属性里面也没有找到 --> 就会在类或父类的非数据描述器中进行查找;

 

上一篇:《痞子衡嵌入式半月刊》 第 33 期


下一篇:33. Django 2.1.7 模板 - 动态URL 反向解析