The behavior is by design. First, keeping object behavior in the
class definition simplifies the implementation and also makes instance
checks more meaningful. To borrow your Register example, if the "M"
descriptor is defined by some instances rather than by the class, then
knowing that the object "reg" is an instance of Register does not tell
me anything about whether "reg.M" is a valid attribute or an error. As
a result, I'll need to guard virtually every access of "reg.M" with a
try-except construct just in case "reg" is the wrong kind of register.
Second, the separation of class from instance also helps you keep
object behavior separate from object data. Consider the following
class:
class ObjectHolder(object):
def __init__(self, obj):
self.obj = obj
Don't worry about what this class might be useful for. Just know that
it's meant to hold and provide unrestricted access to arbitrary Python
objects:
>>> holder = ObjectHolder(42)
>>> print(holder.obj) 42
>>> holder.obj = range(5)
>>> print(holder.obj) [0, 1, 2, 3, 4]
Since the class is meant to hold arbitrary objects, it's even valid
that somebody might want to store a descriptor object there:
>>> holder.obj = property(lambda x: x.foo)
>>> print(holder.obj) <property object at 0x02415AE0>
Now suppose that Python invoked the descriptor protocol for
descriptors stored in instance attributes:
>>> holder = ObjectHolder(None)
>>> holder.obj = property(lambda x: x.foo)
>>> print(holder.obj)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'ObjectHolder' object has no attribute 'foo'
In this case, the ObjectHolder would fail to simply hold the property
object as data. The mere act of assigning the property object, a
descriptor, to an instance attribute would change the behavior of
the ObjectHolder. Instead of treating "holder.obj" as a simple data
attribute, it would start invoking the descriptor protocol on accesses
to "holder.obj" and ultimately redirect them to the non-existent and
meaningless "holder.foo" attribute, which is certainly not what the
author of the class intended.
如果您想支持描述符的多个实例,只需使该描述符的构造函数接受一个名称参数(前缀),并使用该名称为添加的属性添加前缀。您甚至可以在类实例中创建一个命名空间对象(字典),以保存所有新属性实例。