ToB企服应用市场:ToB评测及商务社交产业平台

标题: python教程:属性查询顺序,数据描述符 [打印本页]

作者: 梦见你的名字    时间: 2024-5-21 16:15
标题: python教程:属性查询顺序,数据描述符
数据描述符,属性查找优先级

如果在一个类中定义了 __get__() , __set__(), __delete__() 这三种方法之一,那么这个类是一个描述符。
描述符分成两种:
描述符的用处就是,当一个对象的某个属性是一个描述符时,你访问这个描述符范例的属性,就会调用这个描述符的方法。譬如你获取描述符的值时,会调用它的__get__().
我们先看一下这三个方法的docstring:
  1. def __delete__(self, *args, **kwargs): # real signature unknown
  2.     """ Delete an attribute of instance. """
  3.     # 删除一个实例的属性
  4. def __set__(self, *args, **kwargs): # real signature unknown
  5.     """ Set an attribute of instance to value. """
  6.     # 给实例的属性设置一个值
  7. def __get__(self, *args, **kwargs): # real signature unknown
  8.     """ Return an attribute of instance, which is of type owner. """
  9.     # 返回实例的属性,该实例是 `owner` 类型的
复制代码
实例:
  1. class A(object):
  2.     def __init__(self):
  3.         self.value = None
  4.     def __set__(self, instance, value): # self:类A的实例,也是类B的属性a;instance:类 B 的实例 b;value:通过b.a的赋值
  5.         print('set: self,instance,value',self,instance,value)
  6.         self.value = value
  7.         return self.value
  8.     def __get__(self, instance, owner):# instance:类B的实例b;owner:类B
  9.         print('get: self,instance,owner',self,instance,owner)
  10.         return self.value
  11. class B(object):
  12.     a = A()
  13.     def __init__(self):
  14.         self.val = 20
复制代码
1.上述代码中,有两个类,A,B。先看类 B,有一个类属性 a , 且 a 是类 A 的实例,我们先来实例化一下类 B ,看一下 类 B 和实例 b 的属性:
  1. b = B()
  2. print(b.__dict__)
  3. print(B.__dict__)
  4. """
  5. {'val': 20}
  6. {'__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}
  7. """
复制代码
可以看出,实例 b 的属性中,只有一个 val 属性 ;类 B 的属性中,则有一个 a ,且 a 是类 A 的一个对象。
2.接下来,我们调用一下实例 a:
  1. b = B()
  2. b.a
  3. B.a
  4. """
  5. get: self,instance,owner <__main__.A object at 0x03458E68> <__main__.B object at 0x03458F28> <class '__main__.B'>
  6. get: self,instance,owner <__main__.A object at 0x03458E68> None <class '__main__.B'>
  7. """
复制代码
我们看一下什么意思:
3.现在,我们实验给 b.a 赋值
  1. b = B()
  2. b.a = 11
  3. print(b.__dict__)
  4. print(B.__dict__)
  5. B.a = 12
  6. print(b.__dict__)
  7. print(B.__dict__)
  8. """
  9. set: self,instance,value <__main__.A object at 0x037CFD70> <__main__.B object at 0x037CFDD0> 11
  10. {'val': 20}
  11. {'__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}
  12. {'val': 20}
  13. {'__module__': '__main__', 'a': 12, '__init__': <function B.__init__ at 0x07E85078>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}
  14. """
复制代码
可以看出,当调用了 b.a=11 时,调用了描述符的 __set__() , 但是对象 b 的实例属性并没有改变,依然只有 val=20, 同时类B的类属性也没有改变。 但是当调用 B.a = 12 时,类属性 a 变成了12,并没有调用描述符的__set__()方法。
所以,结合上面的 docstring,我们可以看出,数据描述符应该是给实例使用的,类使用它用处不大,至少没法调用它的 __set__()
  1. class B(object):
  2.         a = A()
  3.         def __init__(self):
  4.                 self.val = 20
  5.                 self.a = 11  # 这里同名的属性
复制代码
此时调用 b.a ,会如何?
当类A是一个数据描述符,也就是说类A包罗 __set__ 方法时,此时数据描述符优先级高,所以实例属性 self.a 其实就是对类属性 a 的赋值,会调用数据描述符的 __set__ 方法:
  1. set: self,instance,value <__main__.A object at 0x009DFD70> <__main__.B object at 0x009DFDD0> 11
  2. get: self,instance,owner <__main__.A object at 0x009DFD70> <__main__.B object at 0x009DFDD0> <class '__main__.B'>
  3. 11
复制代码
当类A是一个非数据描述符,那么实例的字典优先级高,所以会使用实例字典中的数据,即效果:
  1. 11
复制代码
属性查询优先级:

  1. class Quantity1(object):
  2.     def __get__(self, instance, owner):
  3.         return 2
  4.     def __set__(self, instance, val):
  5.         pass
  6. class Quantity2(object):
  7.     def __get__(self, instance, owner):
  8.         return 5
  9. class A(object):
  10.     val = 6  # 6 父类属性
  11.     x = None
  12. class B(A):
  13.     val = Quantity2()  # 5 非覆盖型描述符
  14.     val = 4  # 4 类属性
  15.     val = Quantity1()  # 2 覆盖型描述符
  16.     def __init__(self):
  17.         super(B, self).__init__()
  18.         self.val = 3
  19.     def __getattr__(self, name):  # 7 __getattr__
  20.         return 7
  21.     def __getattribute__(self, name):  # 1 __getattribute__
  22.         return 1
  23. b = B()
  24. print(b.val)
复制代码
说了一堆有的没的,其实描述符就是一个特殊的实现,当你的一个对象的属性是描述符时,设置/赋值/读取 这个属性,都会触发这个描述符内部相应实现的方法。从而可以实现一些定制化的内容。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4