Her*_*nan 21 python descriptor
对于这个问题:
为什么描述符不能成为实例属性?
有人回答说:
描述符对象需要存在于类中,而不是实例中
因为这是实施的方式__getattribute__.
一个简单的例子.考虑描述符:
class Prop(object):
def __get__(self, obj, objtype=None):
if obj is None:
return self
return obj._value * obj._multiplier
def __set__(self, obj, value):
if obj is None:
return self
obj._value = value
class Obj(object):
val = Prop()
def __init__(self):
self._value = 1
self._multiplier = 0
Run Code Online (Sandbox Code Playgroud)
考虑每个obj都有多个Prop的情况:我需要使用唯一的名称来标识值和乘数(比如这里.拥有一个每个实例描述符对象将允许将_multiplier(和_value)存储在描述符本身中,简化了一些事情.
要实现每个实例描述符属性,您需要:
我知道之前已经提出过类似的问题,但我还没有找到真正的解释:
nne*_*neo 16
今年早些时候在Python列表中提出了这个确切的问题.我只想引用Ian G. Kelly的回答:
行为是设计的.首先,在类定义中保持对象行为简化了实现,并使实例检查更有意义.借用你的Register示例,如果"M"描述符是由某些实例而不是类定义的,那么知道对象"reg"是Register的一个实例并没有告诉我"reg.M"是否是一个有效属性或错误.因此,我需要使用try-except结构来保护"reg.M"的几乎所有访问,以防万一"reg"是错误的寄存器.
其次,类与实例的分离也有助于将对象行为与对象数据分开.考虑以下课程:
Run Code Online (Sandbox Code Playgroud)class ObjectHolder(object): def __init__(self, obj): self.obj = obj不要担心这个类可能对什么有用.只要知道它意味着保持并提供对任意Python对象的无限制访问:
Run Code Online (Sandbox Code Playgroud)>>> holder = ObjectHolder(42) >>> print(holder.obj) 42 >>> holder.obj = range(5) >>> print(holder.obj) [0, 1, 2, 3, 4]由于该类旨在保存任意对象,因此有人可能希望在那里存储描述符对象:
Run Code Online (Sandbox Code Playgroud)>>> holder.obj = property(lambda x: x.foo) >>> print(holder.obj) <property object at 0x02415AE0>现在假设Python为存储在实例属性中的描述符调用了描述符协议:
Run Code Online (Sandbox Code Playgroud)>>> 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'在这种情况下,ObjectHolder将无法简单地将属性对象保存为数据.仅仅将属性对象(描述符)分配给实例属性的行为将改变 ObjectHolder 的行为.它不是将"holder.obj"视为一个简单的数据属性,而是在访问"holder.obj"时开始调用描述符协议,并最终将它们重定向到不存在且无意义的"holder.foo"属性,这当然是不是班级作者的意图.
如果您希望能够支持描述符的多个实例,只需使该描述符的构造函数采用名称参数(前缀),并使用该名称为添加的属性添加前缀.您甚至可以在类实例中创建一个名称空间对象(字典)来保存所有新属性实例.
eca*_*mur 11
许多高级功能仅在类而不是实例上定义时才有效; 例如,所有特殊方法.除了使代码评估更有效之外,这还明确了实例和类型之间的分离,否则这些实例和类型会崩溃(因为当然所有类型都是对象).
我不确定这是怎么推荐的,但你可以在实例中存储从描述符实例到属性值的映射:
class Prop(object):
def __get__(self, obj, objtype=None):
if obj is None:
return self
return obj._value * obj._multiplier[self]
def __set__(self, obj, value):
if obj is None:
return self
obj._value = value
class Obj(object):
val = Prop()
def __init__(self):
self._value = 1
self._multiplier = {Obj.val: 0}
Run Code Online (Sandbox Code Playgroud)
与其他两个建议的选项相比,这有明显的优势:
__getattribute__是低效的(因为所有属性访问必须通过重写的特殊方法)并且是脆弱的.作为替代方案,您可以使用代理属性:
class PerInstancePropertyProxy(object):
def __init__(self, prop):
self.prop = prop
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.prop].__get__(instance, owner)
def __set__(self, instance, value):
instance.__dict__[self.prop].__set__(instance, value)
class Prop(object):
def __init__(self, value, multiplier):
self.value = value
self.multiplier = multiplier
def __get__(self, instance, owner):
if instance is None:
return self
return self.value * self.multiplier
def __set__(self, instance, value):
self.value = value
class Obj(object):
val = PerInstancePropertyProxy('val')
def __init__(self):
self.__dict__['val'] = Prop(1.0, 10.0)
def prop(self, attr_name):
return self.__dict__[attr_name]
Run Code Online (Sandbox Code Playgroud)