← [ BACK_TO_NOTES ]

Python 描述符与属性访问

描述符协议驱动 property/方法绑定与属性校验;理解 get/set/delete 与查找顺序。

#concept #growing #dev #python #python

[!info] related notes

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

结合场景再看三个关注点

  1. 赋值走 setter。
  2. 存储用 _unit_price_cents 避免递归。
  3. 函数放在类上经描述符变成绑定方法。

核心概念与准确模型

  • 描述符协议由描述符对象的类型实现;类型定义 __set__ 或 __delete__ 时称为数据描述符,仅定义 __get__ 时为非数据描述符。
  • 普通实例读取先按 MRO 找类上的首个同名属性:有 __get__ 的数据描述符优先,其后是实例 dict,再调用非数据描述符或返回类属性。查找失败时点号访问可调用 __getattr__。
  • property/classmethod/staticmethod/functions

边界情况与反直觉行为

  1. 只定义 __get__ 的非数据描述符可被实例 dict 遮蔽
  2. 重写 __getattribute__ 可改变查找/描述符行为;需要默认行为时委托 object.__getattribute__,直接读 self.x 容易递归。
  3. 只读 property 也有拒绝写入的 __set__,仍优先于同名实例 dict 项。slots 通常也由类上的数据描述符访问。
  4. 本例只检查负值;类型注解不阻止 float、bool 或 NaN。写 _unit_price_cents 也可绕过 setter;property 不是安全隔离或完整输入验证。

常见误区

[!warning] 常见误区:为一切字段写 property 错误理解:封装必须样板。
正确模型:有不变量再引入。

工程实践

  • 公开不变量用 property/描述符
  • 框架作者才常写通用描述符
  • 文档化副作用

本节总结

描述符是属性系统的机制核心。日常用 property;读库时能认出协议。

自测题

  1. property 是什么?
  2. 为何 setter 写入 _x?
参考答案
  1. 描述符的便捷封装。
  2. 避免再次触发 setter 造成递归。

延伸阅读与资料来源

资料类型支撑内容
Descriptor HowToHOWTO权威
创建于 2026/7/15 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本