在Python的描述符HowTo指南中有这样一段(粗体添加):
描述符HowTo指南
描述符协议h5>(…)
要创建只读数据描述符,定义
__get__()
和__set__()
,其中__set__()
在调用时引发AttributeError
。用异常引发占位符定义__set__()
方法足以使它成为一个数据描述符。
我不确定表达式是什么:"异常引发占位符">这里的意思是确切的(谷歌搜索"exception占位符">也无助于澄清表达式),因为谷歌上的唯一结果是引用"描述符HowTo指南">本身。
是否意味着在描述符的__set__()
中引发任何异常?异常必须是AttributeError
吗?还是有别的意思?
这意味着,如果你想要一个数据描述符,你所需要的只是它有一个__set__
方法——即使__set__
方法在被调用时总是引发异常,它仍然是一个数据描述符。
这个总是引发异常的方法;是"异常引发占位符"的含义。
没有__set__
方法的类是"非数据描述符";-主要的变化是,对于非数据描述符,在检索属性时总是检查实例-即使该类中有一个(非数据)描述符,具有相同的名称,也会检索实例属性。因此,可以重写或"删除"。来自类的特定实例的方法:您只需为实例中的方法名分配一些其他值,该值将用于代替原始方法。
现在,如果描述符类确实有__set__
方法,那么尝试在实例上设置值将始终通过它,并且实例值不是通过普通方法设置的。如果该方法引发异常,这将使描述符值不可变:在任何实例中都不能为其分配新值。(当然,在类上赋值它的名称将有效地删除整个描述符。)
property
e总是数据描述符:即使不配置属性"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