关于 Python 描述符(Descriptor)
描述符是在 Python 2.2 版本就被引用的特性,然而作为“元老”,却逐渐消失在 Python 教程的视野中。但当你了解它时,你就懂得了什么是 Python 的优雅之美。
什么是描述符
初识描述符
描述符的定义并不好理解,不如我们先见识一下它的功能:
1 |
|
如果你接触过 SQLAlchemy 类似的 ORM 库,你一定对上述代码实现的功能很眼熟。
没错,我们借助描述符,实现了简单的类似于 ORM 类型转换的功能,无论对 username
赋值什么类型,都会被自动转换成字符串。
在最新的 Python 3.7 文档中这样介绍道:
一般地,一个描述符是一个包含 “绑定行为” 的对象,对其属性的存取被描述符协议中定义的方法覆盖。
这些方法有:__get__()
,__set__()
和__delete__()
。
如果某个对象中定义了这些方法中的任意一个,那么这个对象就可以被称为一个描述符。
- 描述符是一个有“绑定行为”的对象属性(object attribute),它的访问控制会被描述器协议方法重写。
- 任何定义了
__get__
,__set__
或者__delete__
任一方法的类称为描述符类,其实例对象便是一个描述符,这些方法称为描述符协议。 - 当对一个实例属性进行访问时,Python 会按
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: 对描述符的赋值
__delete__(self, instance)
实例
1 |
|
1 |
|
参考文章
https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html
关于 Python 描述符(Descriptor)
https://zkqiang.cn/posts/4c88b2f/