在 Python 众多原生特性中,描述符多是最少被自定义的特性之一,但它在底层实现的方法和属性却无时不刻被使用着,它优雅的实现方式体现出 Python 简洁之美。html
__get__
, __set__
或者 __delete__
任一方法的类称为描述符类,其实例对象即是一个描述符,这些方法称为描述符协议。obj.__dict__
→ type(obj).__dict__
→ type(obj)的父类.__dict__
顺序进行查找,若是查找到目标属性并发现是一个描述符,Python 会调用描述符协议来改变默认的控制行为。@property
@classmethod
@staticmethod
和 super
的底层实现机制。__get__
和 __set__
的描述符称为 数据描述符(data descriptor);仅定义了 __get__
的称为 非数据描述符(non-data descriptor) 。二者区别在于:若是 obj.__dict__
中有与描述符同名的属性,若描述符是数据描述符,则优先调用描述符,如果非数据描述符,则优先使用 obj.__dict__
中属性。__get__(self, instance, owner)
程序员
_:param self: _描述符对象自己编程
_:param instance: _使用描述符的对象的实例缓存
_:param owner: _使用描述符的对象拥有者微信
__set__(self, instance, value)
并发
_:param value: _对描述符的赋值ide
__delete__(self, instance)
ui
class LazyProperty(object): """ 实现惰性求值(访问时才计算,并将值缓存) 利用了 obj.__dict__ 优先级高于 non-data descriptor 的特性 第一次调用 __get__ 以同名属性存于实例字典中,以后就再也不调用 __get__ """ def __init__(self, fun): self.fun = fun def __get__(self, instance, owner): if instance is None: return self value = self.fun(instance) setattr(instance, self.fun.__name__, value) return value class ReadonlyNumber(object): """ 实现只读属性(实例属性初始化后没法被修改) 利用了 data descriptor 优先级高于 obj.__dict__ 的特性 当试图对属性赋值时,总会先调用 __set__ 方法从而抛出异常 """ def __init__(self, value): self.value = value def __get__(self, instance, owner): return self.value def __set__(self, instance, value): raise AttributeError( "'%s' is not modifiable" % self.value ) class Circle(object): pi = ReadonlyNumber(3.14) def __init__(self, radius): self.radius = radius @LazyProperty def area(self): print('Computing area') return self.pi * self.radius ** 2
https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.htmlcode
编程思惟不该只存留在代码之中,更应伴随于整我的生旅途,这个公众号不仅聊技术,还会聊产品/互联网/经济学等普遍话题,因此也欢迎非程序员关注。htm