Python中可通过双下划线方法自定义对象运算行为,关键在于逻辑符合直觉与一致性:__add__返回新对象,__iadd__原地修改并返回self;比较需配合total_ordering且注意__hash__;__str__面向用户,__repr__应可复现对象。

Python中通过特殊方法(双下划线方法)可以为自定义类的对象实现加减乘除、比较、打印等运算行为,让对象像内置类型一样自然参与表达式。关键不是“能重载”,而是重载后逻辑是否符合直觉、是否保持一致性。
让对象支持 + 和 += 运算
用 __add__ 实现加法,返回新对象;用 __iadd__ 实现就地加法(即 +=),建议直接修改自身并返回 self,避免创建冗余对象。
例如定义一个二维向量类:
class Vec2:
def __init__(self, x, y):
self.x, self.y = x, y
<pre class='brush:python;toolbar:false;'>def __add__(self, other):
if isinstance(other, Vec2):
return Vec2(self.x + other.x, self.y + other.y)
return NotImplemented # 告诉Python尝试调用other.__radd__
def __iadd__(self, other):
if isinstance(other, Vec2):
self.x += other.x
self.y += other.y
return self
return NotImplemented注意:若不实现 __iadd__,a += b 会退化为 a = a + b,产生新对象——对大对象或需原地修改的场景不友好。
立即学习“Python免费学习笔记(深入)”;
支持比较操作:==、= 等
只实现 __eq__ 和 __lt__,再配合 functools.total_ordering 装饰器,就能自动补全其余比较方法(!=、、>=)。
常见错误是只重载 __eq__ 却忽略 __hash__:若对象要放入 set 或作 dict 键,且 __eq__ 被重写,通常也需定义 __hash__(除非明确不可哈希)。
- 若对象逻辑上不可变(如坐标、ID),可基于字段计算 hash
- 若对象可变,应显式设
__hash__ = None,防止被意外用作键
让 print(obj) 输出有意义内容
__str__ 用于用户友好的字符串表示(如 print()、str()),__repr__ 应尽量返回可复现对象的表达式(如调试时显示 Vec2(1, 2)),至少保证无歧义、含类名和关键状态。
典型写法:
def __repr__(self):
return f"{self.__class__.__name__}({self.x!r}, {self.y!r})"
<p>def <strong>str</strong>(self):
return f"({self.x}, {self.y})"</p>其他实用重载场景
根据实际需求灵活添加:
-
__len__:支持
len(obj),返回整数 -
__bool__:控制
if obj:的真假值,默认返回 True,可按业务逻辑定制(如容器为空时返回 False) - __call__:让实例像函数一样被调用,适合封装配置化行为或策略对象
-
__getitem__ / __setitem__:支持
obj[key]语法,常用于自定义容器或映射结构
不复杂但容易忽略的是:所有重载方法都应优先检查参数类型,对不支持的操作返回 NotImplemented(不是 NotImplementedError),这样 Python 才能正确触发反射方法(如 __radd__)或抛出 TypeError。









