描述符是在 Python 2.2 版本就被引用的特性,然而作为“元老”,却逐渐消失在 Python 教程的视野中。但当你了解它时,你就懂得了什么是 Python 的优雅之美。

如果你接触过 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 的底层实现机制

Quick Start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"""
实现惰性求值(访问时才计算,并将值缓存)
利用了 obj.__dict__ 优先级高于 non-data descriptor 的特性
第一次调用 __get__ 以同名属性存于实例字典中,之后就不再调用 __get__
"""

class lazyproperty(object):
def __init__(self, fun):
self.fun = fun

def __get__(self, instance, owner):
print(self, instance, owner)
if instance is None:
return self
value = self.fun(instance)
setattr(instance, self.fun.__name__, value)
return value

class Circle(object):
def __init__(self, radius):
self.radius = radius

@lazyproperty
def area(self):
print('Computing area')
return 3.1415 * self.radius ** 2

参考文章

https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!