zoukankan      html  css  js  c++  java
  • python 属性查询顺序,数据描述符

    数据描述符,属性查找优先级

    如果在一个类中定义了 __get__() , __set__(), __delete__() 这三种方法之一,那么这个类是一个描述符。如果这种类只定义了get方法,那么就是一个非数据描述符,反之则是数据描述符。

    实例:

    class A(object):
        def __init__(self):
            self.value = None
    
        def __set__(self, instance, value): # self:类A的实例,也是类B的属性a;instance:类B的实例b;value:通过b.a的赋值
            print('set: self,instance,value',self,instance,value)
            self.value = value
            return self.value
    
        def __get__(self, instance, owner):# instance:类B的实例b;owner:类B
            print('get: self,instance,owner',self,instance,owner)
            return self.value
    
    class B(object):
        a = A()
    
        def __init__(self):
            self.val = 20
    
    
    1. 上述代码中,有两个类,A,B。先看类 B,有一个类属性 a , 且 a 是类 A 的实例,我们先来实例化一下类 B ,看一下 类B 和实例 b 的属性:

      b = B()
      print(b.__dict__)
      print(B.__dict__)
      
      {'val': 20}
      {'__module__': '__main__', 'a': <__main__.A object at 0x0163FD70>, '__init__': <function B.__init__ at 0x07845078>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}
      

      可以看出,实例 b 的属性中,只有一个 val ;类 B 的属性中,有一个 a ,且 a 是类 A 的实例化对象。

    2. 接下来,我们调用一下 a:

      b = B()
      print(b.a,B.a)
      
      get: self,instance,owner <__main__.A object at 0x011CFD70> <__main__.B object at 0x011CFDD0> <class '__main__.B'>
      get: self,instance,owner <__main__.A object at 0x011CFD70> None <class '__main__.B'>
      None None
      

      结果竟然打出来三行话,我们看一下什么意思:

      第一行,是通过 b.a 调用产出的。当调用 b.a 时,程序会自动去调用 b.__getattribute__('a') , 也就是 b.__dict__['a'], 即通过对象 b 的字典去查找属性,但是在第一步我们已经知道, 对象 b 只有一个属性 {'val': 20} ,既然在实例b中找不到‘a'。 因为类A 是一个数据描述符,并且 a 是B的类属性,所以会去调用:type(b).__dict__['a'].__get__(b,type(b)),得到第一行打印数据

      第二行,是通过 B.a 调用产出的。当调用 B.a 时,会直接调用 B.__dict__['a'].__get__(None,B),所以中间有个 None

      第三行,很简单了,前两次的 return 都是None,就打印了None

    3. 现在,我们尝试给 b.a 赋值

      b = B()
      b.a = 11
      print(b.__dict__)
      print(B.__dict__)
      B.a = 12
      print(b.__dict__)
      print(B.__dict__)
      
      set: self,instance,value <__main__.A object at 0x037CFD70> <__main__.B object at 0x037CFDD0> 11
      {'val': 20}
      {'__module__': '__main__', 'a': <__main__.A object at 0x037CFD70>, '__init__': <function B.__init__ at 0x07E85078>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}
      {'val': 20}
      {'__module__': '__main__', 'a': 12, '__init__': <function B.__init__ at 0x07E85078>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}
      

      可以看出,当调用了 b.a=11 时,调用了 描述符 的__set__() , 但是对象b 的实例属性并没有改变,依然只有 val=20, 同时类B的类属性也没有改变。 但是当调用 B.a = 12 时,类属性 a变成了12,并没有调用 描述符的 __set__() 方法。

    4. 如果类属性的描述符对象和实例对象的属性同名,如果查找?

      也就是说,如果把类B改成:

      class B(object):
          a = A()
      
          def __init__(self):
              self.val = 20
              self.a = 11
      

      此时调用 b.a ,会如何?

      1. 当类A是一个数据描述符,也就是说 类A包含 set 方法,此时数据描述符优先级高,所以结果:

        set: self,instance,value <__main__.A object at 0x009DFD70> <__main__.B object at 0x009DFDD0> 11
        get: self,instance,owner <__main__.A object at 0x009DFD70> <__main__.B object at 0x009DFDD0> <class '__main__.B'>
        11
        
      2. 当类A是一个非数据描述符,也就是说将类A 中的 set 方法删掉或者注释掉,那么实例的字典优先级高,所以会使用 实例字典中的数据,即结果:

        11
        

    属性查询优先级:

    1. obj.__getattribute__()

    2. 数据描述符

    3. 实例的字典

    4. 类的字典(4,5排序并不准确,当两者同名且同为类属性时,后声明赋的值,会覆盖前面的赋值,譬如a=4;a=5;执行完成是5,因为程序是从上往下按顺序执行的)

    5. 非数据描述符

    6. 父类的字典

    7. __getattr__

    补充一个顺序的代码:感兴趣的可以按顺序注释掉代码,运行试试

    原链接:https://www.cnblogs.com/wickedpriest/p/11984887.html

    class Quantity1(object):
        def __get__(self, instance, owner):
            return 2
    
        def __set__(self, instance, val):
            pass
    
    
    class Quantity2(object):
        def __get__(self, instance, owner):
            return 5
    
    
    class A(object):
        val = 6  # 6 父类属性
        x = None
    
    
    class B(A):
        val = Quantity2()  # 5 非覆盖型描述符
        val = 4  # 4 类属性,4和5排序并不准确,当两者都为类属性时,后声明的变量会覆盖之前的赋值,因为程序是从上上下执行的。
        val = Quantity1()  # 2 覆盖型描述符
    
        def __init__(self):
            super(B, self).__init__()
            self.val = 3
    
        def __getattr__(self, name):  # 7 __getattr__
            return 7
    
        def __getattribute__(self, name):  # 1 __getattribute__
            return 1
    
    b = B()
    print(b.val)
    
  • 相关阅读:
    继承-方法重写
    继承2
    继承
    JAVA-基本数据类型与引用数据类型区别
    JS判断一个数是否为质数
    Yslow
    Sublime Less 自动编译成css
    chrom调试
    解决在微信中部分IOS不能自动播放背景音乐
    常用的jq插件
  • 原文地址:https://www.cnblogs.com/wztshine/p/14322586.html
Copyright © 2011-2022 走看看