这意味着,如果你想要一个数据描述符,你所需要的只是它有一个 __set__ 方法——即使 __set__ 方法在调用时总是会引发异常,它仍然是一个数据描述符。
这种“总是引发异常的方法”是文档中“引发异常的占位符”的意思。
没有__set__ 方法的类是“非数据描述符”——主要变化是对于非数据描述符,实例是总是在检索属性时检查 - 即使该类中有(非数据)描述符,具有相同的名称,也会检索实例属性。因此,可以从类的特定实例中覆盖或“删除”方法:您只需为实例中的方法名称分配一些其他值,该值将用于代替原始方法。
现在,如果描述符类确实有__set__方法,试图在实例上设置值总是会经过它,而实例值不是通过普通方式设置的。如果该方法引发异常,这会使描述符值不可变:在任何情况下都无法为其分配新值。 (当然,在类上分配它的名称将有效地删除整个描述符)
有趣的是,Python propertyes 是总是数据描述符:属性对象本身确实有一个__set__ 方法,即使没有配置属性“setter”。确切的情况是,当尝试在此类属性中设置值时,将引发异常:没有设置器的 property 是包含 exception raising 方法的描述符的示例。
一些sn-ps来演示要点:
In [7]: class NonDataDescriptor:
...: def __get__(self, inst, owner):
...: return 23
...:
In [8]: class A:
...: b = NonDataDescriptor()
...:
In [9]: c = A()
In [10]: c.b
Out[10]: 23
In [11]: c.__dict__
Out[11]: {}
In [12]: c.b = 42
In [13]: c.__dict__
Out[13]: {'b': 42}
In [14]: c.b
Out[14]: 42
In [15]: # descriptor is superseeded by value attributed to the instance
In [16]: A().b # in a new instance, the descriptor works as new
Out[16]: 23
In [19]: class ImutableDataDescriptor:
...: def __get__(self, inst, owner):
...: return 55
...: def __set__(self, inst, value):
...: # Look! An exception raising placeholder!!
...: raise NotImplementedError() # or whatever exception
...:
In [20]: class D:
...: e = ImutableDataDescriptor()
...:
In [21]: f = D()
In [22]: f.e
Out[22]: 55
In [23]: f.e = 3125
---------------------------------------------------------------------------
NotImplementedError Traceback (most recent call last)
Cell In [23], line 1
----> 1 f.e = 3125
Cell In [19], line 6, in ImutableDataDescriptor.__set__(self, inst, value)
4 def __set__(self, inst, value):
5 # Look! An exception raising placeholder!!
----> 6 raise NotImplementedError()
NotImplementedError:
In [24]: f.__dict__
Out[24]: {}
In [26]: class G:
...: # ordinary properties with no setters work as data-descriptors
...: @property
...: def h(self):
...: return 17
...:
In [27]: i = G()
In [28]: i.h
Out[28]: 17
In [29]: i.h = 23
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In [29], line 1
----> 1 i.h = 23
AttributeError: property 'h' of 'G' object has no setter