Python 描述符与属性访问
描述符协议驱动 property/方法绑定与属性校验;理解 get/set/delete 与查找顺序。
#type / concept
#status / growing
#tech / dev
#resource / python
#tech / lang / python
[!info] 关联笔记
Python 描述符与属性访问
这个概念为什么出现
@property、方法绑定、ORM 字段——底层多是描述符。不懂描述符,属性“读的是类上的函数却变成方法”会像魔法。
[!abstract] 一句话理解 描述符是实现
__get__/__set__/__delete__的对象;放在类上时介入属性访问,property 是其应用。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:单价必须非负的属性校验
# price_descriptor_demo.py
# 业务意图:用 property 校验单价。
# 教学点:描述符应用;读写分离。
class LineItem:
def __init__(self, sku: str, unit_price_cents: int) -> None:
self.sku = sku
self.unit_price_cents = unit_price_cents
@property
def unit_price_cents(self) -> int:
return self._unit_price_cents
@unit_price_cents.setter
def unit_price_cents(self, value: int) -> None:
if value < 0:
raise ValueError("price must be >= 0")
self._unit_price_cents = value
def main() -> None:
item = LineItem("SKU-1", 100)
print(item.unit_price_cents)
try:
item.unit_price_cents = -1
except ValueError as exc:
print("blocked:", exc)
if __name__ == "__main__":
main()
建议运行:
python price_descriptor_demo.py
期望输出:
100
blocked: price must be >= 0
结合场景再看三个关注点
- 赋值走 setter。
- 存储用
_unit_price_cents避免递归。 - 函数放在类上经描述符变成绑定方法。
核心概念与准确模型
- 数据描述符 vs 非数据描述符
- 查找顺序:数据描述符优先于实例 dict
property/classmethod/staticmethod/functions
边界情况与反直觉行为
- 只定义
__get__的非数据描述符可被实例 dict 遮蔽 __getattribute__重写极危险- slots + 描述符 组合细节
常见误区
[!warning] 常见误区:为一切字段写 property 错误理解:封装必须样板。
正确模型:有不变量再引入。
工程实践
- 公开不变量用 property/描述符
- 框架作者才常写通用描述符
- 文档化副作用
本节总结
描述符是属性系统的机制核心。日常用 property;读库时能认出协议。
自测题
- property 是什么?
- 为何 setter 写入
_x?
参考答案
- 描述符的便捷封装。
- 避免再次触发 setter 造成递归。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Descriptor HowTo | HOWTO | 权威 |