描述符是一个实现了__get__、__set__和__delete__之一的方法的对象。它允许控制对类属性的访问。
描述符的标准方法:
__get__(self, instance, owner)__set__(self, instance, value)__delete__(self, instance)描述符可以分为:
使用示例:
class OnlyPositive: def __init__(self): self._name = '_value' def __get__(self, instance, owner): return instance.__dict__[self._name] def __set__(self, instance, value): if value < 0: raise ValueError('Value must be >= 0') instance.__dict__[self._name] = value class Account: value = OnlyPositive() def __init__(self, value): self.value = value acc = Account(10) acc.value = -1 # ValueError!
property 仅仅是用于在类级别创建描述符的语法糖。
如果将描述符作为实例的属性而不是类的属性创建,会发生什么?
许多人认为描述符会工作,但事实并非如此。
正确答案:
描述符只有在作为类属性直接定义时才能工作。如果将描述符作为实例属性定义,get/__set__方法不会被调用——将调用普通的属性访问逻辑。
故事
使用property而不是完整的描述符
为了验证和其他相关属性,开发者使用了简单的property,导致逻辑的频繁重复,无法在不同的类之间重用代码。
故事
在重用存储时未遵循不可变性
描述符将数据存储在其自有类的内部属性(self.x)中,而不是实例的内部存储中,这导致属性变得 "共享",不同的类实例相互覆盖值——数据在对象之间 "泄漏"。
故事
混淆数据描述符和非数据描述符
在复杂的层次中遗漏了__set__的实现,导致普通的实例属性覆盖了描述符,破坏了整个验证机制——错误并不总是出现,调试困难。