描述符协议深度
理解 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 链搜。
属性查找完整流程:先在 type(obj).__mro__ 中找数据描述符再看 obj.__dict__再在 type(obj).__mro__ 中找非数据描述符/类属性最后调用 __getattr__ 这意味着:即使父类有数据描述符,子类实例的 __dict__ 也无法覆盖但非数据描述符(方法)可以被实例属性覆盖多继承时按 MRO 顺序查找
property 属于哪种描述符?
实例 __dict__ 和非数据描述符哪个优先?
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
with 语句等价于 try/finally:无论正常结束还是中途抛异常,退出时都会执行清理(关文件、释放锁、断开连接)。实现方式有两种:类里写 __enter__/__exit__,或用 contextlib.contextmanager 把生成器变成上下文管理器。凡是「打开了必须关闭/获取了必须释放」的资源,都应该用 with 托管。
挑战任务
类型校验描述符
写个 TypedField 描述符,能校验 int/str/float 类型,最后测一下。
课后作业
惰性加载描述符
实现一个 LazyField 描述符,首次访问时才计算值并缓存。