描述符操作指南中的"引发异常占位符"是什么意思?



在Python的描述符HowTo指南中有这样一段(粗体添加):

描述符HowTo指南

描述符协议h5>(…)

要创建只读数据描述符,定义__get__()__set__(),其中__set__()在调用时引发AttributeError。用异常引发占位符定义__set__()方法足以使它成为一个数据描述符。

我不确定表达式是什么:"异常引发占位符">这里的意思是确切的(谷歌搜索"exception占位符">也无助于澄清表达式),因为谷歌上的唯一结果是引用"描述符HowTo指南">本身。

是否意味着在描述符的__set__()中引发任何异常?异常必须是AttributeError吗?还是有别的意思?

这意味着,如果你想要一个数据描述符,你所需要的只是它有一个__set__方法——即使__set__方法在被调用时总是引发异常,它仍然是一个数据描述符。

这个总是引发异常的方法;是"异常引发占位符"的含义。

没有__set__方法的类是"非数据描述符";-主要的变化是,对于非数据描述符,在检索属性时总是检查实例-即使该类中有一个(非数据)描述符,具有相同的名称,也会检索实例属性。因此,可以重写或"删除"。来自类的特定实例的方法:您只需为实例中的方法名分配一些其他值,该值将用于代替原始方法。

现在,如果描述符类确实有__set__方法,那么尝试在实例上设置值将始终通过它,并且实例值不是通过普通方法设置的。如果该方法引发异常,这将使描述符值不可变:在任何实例中都不能为其分配新值。(当然,在类上赋值它的名称将有效地删除整个描述符。)

有趣的是,Pythonpropertye总是数据描述符:即使不配置属性"setter",属性对象本身也有__set__方法。当试图在这样的属性中设置值时,就会引发异常:没有setter的property就是包含exception raising方法的描述符的例子。

一些演示要点的代码片段:

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

最新更新