大家好,我是正在实战各种 AI 项目的程序员晚枫。
你有没有想过,obj.age = -1 时是谁在拦截这次赋值、抛出 ValueError?property 的底层到底在做什么?Django ORM 的字段验证又是怎么实现的?
答案只有一个词:描述符(Descriptor)。
描述符是 Python 属性访问的核心机制,也是 property、classmethod、staticmethod、甚至槽(__slots__)的底层实现。理解它,你就真正理解了 Python 的对象模型。
🎯 什么是描述符?
描述符协议
一个对象,只要定义了以下三个方法中的至少一个,就是一个描述符:
| 方法 | 作用 | 触发场景 |
|---|---|---|
__get__(self, instance, owner) | 获取属性值 | obj.attr 或 Class.attr |
__set__(self, instance, value) | 设置属性值 | obj.attr = value |
__delete__(self, instance) | 删除属性 | del obj.attr |
还有一个辅助方法:
| 方法 | 作用 | 触发场景 |
|---|---|---|
__set_name__(self, owner, name) | 绑定名称 | 类定义时(Python 3.6+) |
数据描述符 vs 非数据描述符
这是理解描述符优先级的关键:
- 数据描述符:同时定义了
__get__和__set__(或__delete__)——优先级高于实例__dict__ - 非数据描述符:只定义了
__get__——优先级低于实例__dict__
1 | # 数据描述符(有 __set__) |
🔑 实战:验证描述符
这是描述符最典型的应用——自动校验属性值:
1 | class Validator: |
🔧 property 的本质
property 其实是一个内置的描述符类:
1 | # 传统写法 |
🔍 属性查找顺序
这是理解描述符的关键。当你访问 obj.attr 时,Python 的查找顺序是:
1 | 1. type(obj).__mro__ 中找到数据描述符(有 __set__/__delete__)→ 直接调用其 __get__ |
1 | class DataDesc: |
🏗️ 描述符工厂:动态生成描述符
当多个字段需要相似的验证逻辑时,可以用工厂函数简化:
1 | def quantity(storage_name): |
🗄️ ORM 原理揭秘
Django ORM 字段验证、SQLAlchemy Column 的核心实现,都基于描述符:
1 | class Field: |
💡 实战技巧
1. 用 __set_name__ 避免重复配置(Python 3.6+)
1 | # 旧写法:需要手动指定名称(容易出错) |
2. 描述符中用实例 __dict__ 存储,避免无限递归
1 | class Descriptor: |
3. 类访问时返回描述符自身
1 | def __get__(self, instance, owner): |
⚠️ 常见陷阱
1. 忘记处理 instance is None 的情况
1 | # 错误:通过类访问时会报 AttributeError |
2. 描述符必须定义在类中,不能在实例中
1 | class MyDescriptor: |
3. 非数据描述符会被实例属性遮蔽
1 | class Method: # 模拟函数描述符 |
🎯 本讲总结
描述符协议:实现 __get__、__set__、__delete__ 的对象,能够拦截属性访问。
数据 vs 非数据描述符:有 __set__ 的数据描述符优先级高于实例 __dict__;只有 __get__ 的非数据描述符会被实例属性覆盖。
属性查找顺序:数据描述符 → 实例 __dict__ → 非数据描述符/类属性。
property 本质:内置的数据描述符类,三个方法(getter/setter/deleter)对应 __get__/__set__/__delete__。
实际应用:属性验证、ORM 字段、缓存属性(functools.cached_property 就是非数据描述符)。
**__set_name__**:Python 3.6+ 新增,让描述符自动感知所属类和属性名,告别硬编码。
📚 推荐教材
《Python 编程从入门到实践(第 3 版)》 | 《流畅的 Python(第 2 版)》 | 《CPython 设计与实现》
学习路线: 零基础 → 《从入门到实践》 → 《流畅的 Python》 → 本门课程 → 《CPython 设计与实现》
🎓 加入《流畅的 Python》直播共读营
学到这里,如果你想系统吃透这本书——欢迎加入我的直播共读课。
- 每周直播精讲,逐章拆解核心知识点
- 专属学习群,随时答疑交流
- 试运营特惠:
499 元→ 299 元
👉 【立即报名《流畅的 Python》共读课】:https://mp.weixin.qq.com/s/ivHJwn1nNx5ug4TFrapvGg
🔗 课程导航
← 上一讲:接口与协议 | 下一讲:动态属性和特性 →
💬 联系我
| 平台 | 账号/链接 |
|---|---|
| 微信 | 扫码加好友 |
| B 站 | Python 自动化办公社区 |
主营业务:AI 编程培训、企业内训、技术咨询
