必须手动实现富比较方法,否则==仅判断是否为同一对象;当类表示有明确相等或大小关系的数据(如坐标点、分数)时,需定义__eq__、__lt__等以支持逻辑比较。

python中自定义类的比较行为,靠的是富比较方法(Rich Comparison Methods),比如 __eq__、__lt__、__le__ 等。它们不是自动继承或默认实现的,必须手动定义,否则两个实例用 == 比较时只是判断是否为同一对象(即 is),而不是按逻辑相等。
为什么要实现富比较方法
当你的类表示有明确“大小”或“相等”含义的数据(如坐标点、分数、日期、商品价格)时,直接用 ==、、<code>!= 等运算符会更自然、可读性更强。否则就得写 a.equals(b) 或 a.is_less_than(b) 这类冗长调用。
Python不会根据你实现了 __eq__ 就自动推导出 __ne__;也不会因写了 __lt__ 和 __eq__ 就自动支持 __le__。每个方法都需显式定义,或借助 functools.total_ordering 装饰器减少重复代码。
六个核心富比较方法及其对应运算符
它们都接收 self 和另一个操作数 other,返回布尔值:
立即学习“Python免费学习笔记(深入)”;
-
__eq__(self, other)→== -
__ne__(self, other)→!= -
__lt__(self, other)→ -
__le__(self, other)→ -
__gt__(self, other)→> -
__ge__(self, other)→>=
注意:这些方法在不支持比较时应返回 NotImplemented(不是 NotImplementedError!),以便 Python 尝试调用 other 的对应反向方法(如 a 时 <code>a.__lt__(b) 返回 NotImplemented,则尝试 b.__gt__(a))。
实用实现技巧与常见陷阱
直接手写全部六个方法容易出错且重复。推荐两种稳妥方式:
- 只实现
__eq__+__lt__,再用 @total_ordering 装饰类,它会自动补全其余四个(__le__、__gt__、__ge__、__ne__) - 在方法内部做类型检查:若
other不是同类实例,返回NotImplemented,不要抛异常或硬转类型 - 避免在
__eq__中用==递归比较自身属性(比如属性本身也重载了==),要确保逻辑终止;必要时用isinstance(other, type(self))判断后再解构比较 - 对不可变对象(如
Point),可在__eq__中直接比字段元组:return (self.x, self.y) == (other.x, other.y),简洁安全
一个完整可运行示例:二维点类
以下代码展示了带 @total_ordering 的典型写法:
from functools import total_ordering <p>@total_ordering class Point: def <strong>init</strong>(self, x, y): self.x = x self.y = y</p><pre class="brush:php;toolbar:false;">def __eq__(self, other): if not isinstance(other, Point): return NotImplemented return self.x == other.x and self.y == other.y def __lt__(self, other): if not isinstance(other, Point): return NotImplemented # 先比 x,x 相同再比 y(字典序) return (self.x, self.y) < (other.x, other.y)
使用示例
p1 = Point(1, 2) p2 = Point(1, 3) p3 = Point(1, 2)
print(p1 == p3) # True print(p1 != p2) # True print(p1 p1) # True
这个例子中,@total_ordering 让我们只写 __eq__ 和 __lt__,就支持全部六种比较,且类型安全、语义清晰。