0

我很困惑为什么在实例实例化之后检索或在实例构造期间设置的实例属性是通过描述符完成的。

例如,假设我们有以下描述符和类。

描述符

from weakref import WeakKeyDictionary

class Positive:

   def  __init__(self):
      self._instance_data = WeakKeyDictionary()

   def __get__(self, instance, owner):
      return self._instance_data[instance]

   def __set__(self, instance, value):
      if value <= 0:
         raise ValueError(f'Value {value} is not positive')
      self._instance_data[instance] = value

   def __delete__(self, instance):
      raise AttributeError('Cannot delete attribute!')

班级

class Planet:

   def __init__(self, name, mass_kilograms):
      self.name = name
      self.mass_kilograms= mass_kilograms

   mass_kilograms = Positive()

现在我们创建一个 Planet 实例并检索它的质量。

pluto = Planet(name = 'Pluto', mass_kilograms = 1.305e22) 
# The above line is doing Positive.__set__(self, pluto, 1.305e22) under the hood.
# It is NOT doing self.mass_kilograms = 1.305e22. In fact, all of the instance
# attributes are stored in the descriptor Positive's _instance_data
m = pluto.mass_kilograms # m = Positive.__get__(self, pluto, Planet)

我不知道为什么Positive.__get__要调用它,因为它是一个类属性。有人可以解释一下吗?更令人困惑的是如何Positive.__set__拦截构造函数参数的直接赋值mass_kilograms

谢谢!

4

1 回答 1

0

mass_kilograms不是实例属性。它是绑定到描述符实例的类属性Planet

class Planet:

   def __init__(self, name, mass_kilograms):
      self.name = name
      self.mass_kilograms= mass_kilograms
     a

   mass_kilograms = Positive()  # This creates a class attribute

因为Positive定义__set__,它是一个数据描述符。根据描述符how-to:

对于物体来说,机器在object.__getattribute__()其中转化b.xtype(b).__dict__['x'].__get__(b, type(b))。该实现通过优先链工作,该优先链赋予数据描述符优先于实例变量,实例变量优先于非数据描述符,并分配最低优先级(__getattr__()如果提供)。完整的 C 实现可以在PyObject_GenericGetAttr()中找到Objects/object.c

Planet.__init__中,对这个类属性的赋值触发了__set__对描述符方法的调用,就好像你写过

def __init__(self, name, mass_kilograms):
    self.name = name
    self.mass_kilograms.__set__(self, mass_kilograms)

更基本的是,最后一行相当于

Positive.__set__(type(self).mass_kilograms, self, mass_kilograms)
于 2019-12-15T19:39:00.920 回答