类 with descriptors 如何使用描述符的方法解析它们的实例属性?
How do classes with descriptors have their instance attributes resolved with the descriptor's methods?
我很困惑为什么在实例实例化之后检索或在实例构造期间设置的实例属性是通过描述符完成的。
例如,假设我们有以下描述符和 class.
描述符
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
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__
,因为这是一个 class 属性。有人可以解释一下吗?更让人疑惑的是Positive.__set__
是如何拦截构造函数参数mass_kilograms
的直接赋值的。
谢谢!
mass_kilograms
不是实例属性。它是绑定到描述符 Planet
.
实例的 class 属性
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__
,所以是数据描述符。根据描述符操作方法:
For objects, the machinery is in object.__getattribute__()
which transforms b.x
into type(b).__dict__['x'].__get__(b, type(b))
. The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to __getattr__()
if provided. The full C implementation can be found in PyObject_GenericGetAttr()
in Objects/object.c
.
在Planet.__init__
中,对这个class属性的赋值会触发对描述符__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)
我很困惑为什么在实例实例化之后检索或在实例构造期间设置的实例属性是通过描述符完成的。
例如,假设我们有以下描述符和 class.
描述符
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
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__
,因为这是一个 class 属性。有人可以解释一下吗?更让人疑惑的是Positive.__set__
是如何拦截构造函数参数mass_kilograms
的直接赋值的。
谢谢!
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__
,所以是数据描述符。根据描述符操作方法:
For objects, the machinery is in
object.__getattribute__()
which transformsb.x
intotype(b).__dict__['x'].__get__(b, type(b))
. The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to__getattr__()
if provided. The full C implementation can be found inPyObject_GenericGetAttr()
inObjects/object.c
.
在Planet.__init__
中,对这个class属性的赋值会触发对描述符__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)