在 Python 中,运算符重载是指*通过定义特定的魔法方法,让自定义类的实例也能使用 +、-、 等内置运算符,或参与 str()、print() 等内置功能**。这让你设计的类使用起来像 Python 内置类型一样自然。
下面重点讲解最常见、最实用的三个方法:add、str 和 repr。
add:让对象支持 + 运算
- 作用:当你对两对象使用
+运算符时(a + b),Python 会自动调用a.add(b),并将返回值作为表达式结果。 - 适用场景:任何需要合并、拼接或求和的自定义类。例如向量相加、计数器合并、路径拼接等。
- 示例:一个表示二维向量的类
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""返回两个向量相加的结果(新对象)"""
if not isinstance(other, Vector):
return NotImplemented # 如果 other 不是 Vector,交给 other 的 __radd__ 处理
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # 实际调用 v1.__add__(v2)
print(v3.x, v3.y) # 输出 4 6
- 设计要点:返回一个新对象(不可变对象语义)通常更安全,但也可改为
self.x += other.x并返回self,实现原地修改。注意用isinstance检查参数类型,避免意外行为。
str:用户友好字符串
- 作用:当调用
str(obj)、print(obj)或用 f-stringf"{obj}"时触发,应返回易读、描述性的字符串,目的是给人看。 - 默认行为:若未定义
str,Python 会转去调用repr(因此即使没有str,print也有输出)。
class Vector:
# ... 同上 __init__ 和 __add__ ...
def __str__(self):
return f"({self.x}, {self.y})" # 简洁可读
v = Vector(5, 7)
print(v) # 输出 (5, 7)
- 原则:
print的结果应该像“最终展示给终端用户的描述信息”,尽量避免包含调试细节(如内存地址)。
repr:开发者调试字符串
- 作用:当调用
repr(obj)、在交互式解释器中直接输入对象名、或在调试时查看对象的字符串表示时触发。返回值应尽可能无歧义地代表对象内容,理想情况下是能够用eval()重建该对象的一份有效 Python 表达式。 - 设计原则:“信息丰富且明确”,旨在帮助开发者识别对象内部状态。
class Vector:
def __repr__(self):
return f"Vector({self.x!r}, {self.y!r})" # 用 !r 格式化内部值,确保 repr 嵌套正确
v = Vector(8, 9)
print(repr(v)) # 输出 Vector(8, 9)
# 在交互解释器直接敲 v,也会显示 Vector(8, 9)
注意示例中使用 !r 来格式化 self.x 和 self.y,这样即使坐标是字符串类型,也能在 repr 中保留引号,保证 eval 的准确性。
- 最佳实践:如果你只能实现一个,优先实现
repr(因为str找不到时会回退到repr);如果两者都实现,则让repr面向开发者,str面向用户。
三者协作示意
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Point({self.x}, {self.y})"
def __str__(self):
return f"坐标 ({self.x}, {self.y})"
p = Point(3, 4)
print(p) # 调用 __str__ : 坐标 (3, 4)
p2 = p + Point(1, 2)
print(repr(p2)) # 调用 __repr__ : Point(4, 6)
通过合理重载这三个方法,自定义类可以无缝融入 Python 的打印、运算和调试生态,代码既优雅又自然。