在 Python 众多原生特性中,描述符可能是最少被自定义的特性之一,但它在底层实现的方法和属性却无时不刻被使用着,它优雅的实现方式体现出 Python 简洁之美。

定义

  • 一个描述符是一个有"绑定行为"的对象属性(object attribute),它的访问控制会被描述器协议方法重写。
  • 任何定义了 __get__, __set__ 或者 __delete__ 任一方法的类称为描述符类,其实例对象便是一个描述符,这些方法称为描述符协议。
  • 当对一个实例属性进行访问时,Python 会按 obj.__dict__type(obj).__dict__type(obj)的父类.__dict__ 顺序进行查找,如果查找到目标属性并发现是一个描述符,Python 会调用描述符协议来改变默认的控制行为。
  • 描述符是 @property @classmethod @staticmethodsuper 的底层实现机制。

特性

  • 同时定义了 __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: _对描述符的赋值

__delete__(self, instance)

实例

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.html


欢迎关注

编程思维不应只存留在代码之中,更应伴随于整个人生旅途,这个公众号不只聊技术,还会聊产品/互联网/经济学等广泛话题,所以也欢迎非程序员关注。

最新文章

  1. Ubuntu 安装 JDK 7
  2. html画布
  3. Invert Binary Tree
  4. [转] 安装DotNetCore.1.0.1-VS2015Tools.Preview2.0.2出现0x80072f8a未指定的错误
  5. Reactor 与 Proactor
  6. WebFormJS注册位置
  7. zoj 1730 / poj 1455 Crazy Tea Party
  8. 写一个 docker 打击一系列手册
  9. python的Virtualenv
  10. 关于《common-net》的ftp上传
  11. 用c#开发微信 系列汇总 - z
  12. 【HDU1219】AC Me(水题)
  13. 20165316 技能学习心得与c语言学习
  14. HDU 2023 求平均成绩
  15. C#写Excel(OleDB)
  16. 关于使用CTE(公用表表达式)的递归查询
  17. 如何控制jquery ui弹窗下方按钮水平居中
  18. json_encode 中文 null
  19. debian 7 linux 安装jdk出现Error occurred during initialization of VM java/lang/NoClassDefFoun
  20. PHP 获取指定日期的星期几的方法

热门文章

  1. Springboot日志配置探索(主要看logback)(二)
  2. 090 Subsets II 子集 II
  3. 17995 Stupid thief 组合数学
  4. (C#)asp_net调试错误解决方法收集(1)
  5. python super详解
  6. html页面编码问题
  7. iOS的设计备忘录/资源集合(新手快速开发)
  8. HDU 6052 To my boyfriend(容斥+单调栈)
  9. vue 数组更新 this.$set(this.dataList, data.index, data.data)
  10. Python 类变量,成员变量,静态变量,局部变量