25 分钟
Python 高级

描述符协议深度

理解 property/classmethod/staticmethod 的底层原理

  • 理解描述符协议 __get__/__set__/__delete__
  • 区分数数据描述符和非数据描述符
  • 理解属性查找优先级
  • 能实现自定义描述符(ORM字段、类型校验)

描述符协议深度

描述符是 Python 里少有人摸透的特性。property、classmethod、staticmethod、ORM 字段、slots 底层都是描述符。

什么是描述符?

一个类只要实现了 __get__、__set__、__delete__ 中的任何一个,就是描述符。描述符能自定义属性被访问时的行为。

示例代码(可运行)

数据描述符 vs 非数据描述符

示例代码(可运行)
🐍资深工程师经验谈

理解属性查找优先级是理解 Python OOP 的关键。property 是数据描述符(有 __set__),所以它总能拦截赋值。而方法(函数只有 __get__)是非数据描述符,所以实例可以覆盖方法——这就是猴子补丁能生效的原因。

property 的底层实现

示例代码(可运行)

实战:类型校验描述符

示例代码(可运行)
💡描述符的实际用途

Django/SQLAlchemy ORM 的字段、Pydantic 的字段验证、API 参数校验、惰性加载、日志记录属性访问、线程安全的属性、自动类型转换——所有需要「拦截属性读写」的场景都是描述符的用武之地。

MRO交互

描述符与 MRO 的交互

属性查找不光看优先级,还得沿着 MRO 链搜。

属性查找完整流程:先在 type(obj).__mro__ 中找数据描述符再看 obj.__dict__再在 type(obj).__mro__ 中找非数据描述符/类属性最后调用 __getattr__ 这意味着:即使父类有数据描述符,子类实例的 __dict__ 也无法覆盖但非数据描述符(方法)可以被实例属性覆盖多继承时按 MRO 顺序查找

选择题

property 属于哪种描述符?

选择题

实例 __dict__ 和非数据描述符哪个优先?

资深工程师加餐

底层原理 · 大厂视角 · 工程经验,点卡片展开

with 语句等价于 try/finally:无论正常结束还是中途抛异常,退出时都会执行清理(关文件、释放锁、断开连接)。实现方式有两种:类里写 __enter__/__exit__,或用 contextlib.contextmanager 把生成器变成上下文管理器。凡是「打开了必须关闭/获取了必须释放」的资源,都应该用 with 托管。

挑战任务

类型校验描述符

简单+50 XP

写个 TypedField 描述符,能校验 int/str/float 类型,最后测一下。

类型校验描述符
2 个测试用例

课后作业

惰性加载描述符

中等+25 XP

实现一个 LazyField 描述符,首次访问时才计算值并缓存。

惰性加载描述符
1 个测试用例