← [ BACK_TO_NOTES ]

Python 特殊方法

特殊方法(dunder)让对象接入语言协议:构造、展示、运算、容器、上下文与异步钩子。

#concept #growing #dev #python #python

[!info] related notes

Python 特殊方法

这个概念为什么出现

你希望 len(order_book)、item in book、with lock: 对自定义类型生效。Python 不靠接口声明表,而靠实现约定的 __xxx__ 特殊方法 接入语法与内置函数。

[!abstract] 一句话理解 特殊方法是对象协议钩子:解释器在语法/内置场景调用它们,使自定义类型表现得像内置对象。

最小可运行示例

先把示例放进业务场景,再看代码:

场景:购物车支持 len、in 与可读 repr

# cart_dunder_demo.py
# 业务意图:让 Cart 接入 len/in/repr 协议。
# 教学点:
# - __init__/__repr__;
# - __len__/__contains__;
# - 协议比名义继承更重要。

class Cart:
    def __init__(self, user_id: str, items: list[str] | None = None) -> None:
        self.user_id = user_id
        self._items = [] if items is None else list(items)

    def add(self, sku: str) -> None:
        self._items.append(sku)

    def __len__(self) -> int:
        return len(self._items)

    def __contains__(self, sku: object) -> bool:
        return sku in self._items

    def __repr__(self) -> str:
        return f"Cart(user_id={self.user_id!r}, items={self._items!r})"


def main() -> None:
    cart = Cart("u1", ["SKU-1"])
    cart.add("SKU-2")
    print(repr(cart))
    print("len:", len(cart))
    print("has SKU-2:", "SKU-2" in cart)


if __name__ == "__main__":
    main()

建议运行:

python cart_dunder_demo.py

期望输出:

Cart(user_id='u1', items=['SKU-1', 'SKU-2'])
len: 2
has SKU-2: True

结合场景再看三个关注点

  1. len(cart) 调用 __len__,不是普通方法名。
  2. in 走 __contains__(否则可能退化迭代探测)。
  3. repr 面向开发者调试,应信息充分且尽量无歧义。

核心概念与准确模型

领域例子
构造/生命周期__new__ __init__ __del__
展示__repr__ __str__
比较__eq__ __lt__ …
容器__len__ __getitem__ __iter__ __contains__
数值__add__ __radd__ …
上下文__enter__ __exit__
可调用__call__
异步__aenter__ __await__ …

调用约定:优先通过内置/语法触发(len(x))。许多隐式特殊方法查找直接走类型,绕过实例上的同名属性;给单个实例赋 __len__ 不会改变 len(x)。内置入口还会检查返回值,例如 __len__ 必须返回非负整数。

边界情况与反直觉行为

  1. 重写 __eq__ 而未显式提供 __hash__ 通常使类不可哈希。可哈希对象必须保证相等时哈希相同,作为键期间参与哈希/相等的状态应稳定。
  2. __getattr__ vs __getattribute__:后者是全链路,易误用。
  3. 二元运算不支持对方类型时返回 NotImplemented,不是抛 NotImplementedError;调度可尝试另一方的方法。双方均不支持时,排序/算术通常报 TypeError,相等/不等比较有身份回退。
  4. __init__ 初始化已有对象,必须返回 None;__new__ 负责创建。__del__ 不保证及时运行或在解释器退出时执行,资源清理应使用明确的上下文/关闭协议。

常见误区

[!warning] 常见误区:特殊方法是“魔法随便加” 错误理解:加越多越强大。
正确模型:只实现真正需要的协议;错误实现会破坏通用代码假设。

工程实践

  • 至少提供有用 __repr__
  • 容器协议成组实现
  • 上下文管理器优先 contextlib

本节总结

特殊方法是 Python 面向协议编程的核心。实现协议比堆名义继承更地道。

自测题

  1. len(obj) 与 obj.__len__() 更推荐哪个?
  2. 为什么 __repr__ 重要?
参考答案
  1. len(obj)——走官方协议路径。
  2. 调试与日志可读;容器/异常中大量依赖 repr。

延伸阅读与资料来源

资料类型支撑内容
Data model规范特殊方法全集
创建于 2026/5/20 更新于 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
关闭浮层 / 取消命令;输入框内清空文本