Python 特殊方法

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

#type / concept #status / growing #tech / dev #resource / python #tech / lang / python

[!info] 关联笔记

Python 特殊方法

这个概念为什么出现

你希望 len(order_book)item in bookwith 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 = list(items or [])

    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)),不要到处手写 x.__len__()

边界情况与反直觉行为

  1. __eq__ 与哈希:可变对象重载相等要谨慎 __hash__
  2. __getattr__ vs __getattribute__:后者是全链路,易误用。
  3. 返回 NotImplemented 让反射运算有机会尝试。

常见误区

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

工程实践

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

本节总结

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

自测题

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

延伸阅读与资料来源

资料类型支撑内容
Data model规范特殊方法全集
创建于 2026/5/20 更新于 2026/7/15