Python __eq__() 方法

Python __eq__() 语法

__eq__() 是 Python 的一个特殊方法(又称 “魔法方法” 或 “双下划线方法”),它用于定义当两个对象进行 “相等(==)” 比较时,如何判断它们的大小。

当我们使用 “==” 运算符比较两个对象时,Python 内部会自动调用它们的 __eq__() 方法。

语法:

class MyObject:
    def __eq__(self, other):
        # 定义 self == other 的行为,最终返回一个布尔值
        return 布尔值

说明:

__eq__() 方法接收以下 2 个参数。

  • self(必选) :表示调用 “==” 运算符的左侧对象。
  • other(必选) :表示调用 “==” 运算符的右侧对象。

对于 __eq__() 方法,小伙伴们要清楚以下几点。

  • 当实现 __eq__() 时,我们强烈建议同时实现 __hash__() 方法,尤其当你的对象会作为字典的键或集合的元素时。如果两个对象相等(即 a == b 返回 True),那么它们的哈希值也必须相等(即 hash(a) == hash(b) 返回 True)。否则,可能会导致字典和集合的行为异常。
  • 如果 other 不是当前类的实例,或者不是你希望进行比较的类型,通常最佳实践是返回 NotImplemented。这会告诉 Python 尝试调用 other 对象的 __eq__() 方法(如果存在的话),或者回退到默认的比较行为。直接返回 False 也是一种选择,但 NotImplemented 提供了更大的灵活性。
  • 默认情况下,如果没有在类中定义 __eq__() 方法,Python 会使用 is 运算符进行比较。即只有当两个对象是同一个对象的引用时,才被认为是相等的(基于内存地址)。

提示:我们可以借助 functools.total_ordering 装饰器来自动实现其他比较方法,它只需要你实现 __eq__() 和任意一个方法,就可以自动生成所有其他比较方法。

Python __eq__() 摘要

属于 Python 魔法方法
使用频率
官方文档 查看
相关方法 __ne__()__hash__()

Python __eq__() 示例

接下来,我们通过几个简单的例子来讲解一下 Python __eq__() 方法是如何使用的。

示例 1:默认的 “==” 行为(基于内存地址)

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = p1           # p3 是 p1 的另一个引用

print(p1)
print(p2)
print(p3)

print(p1 == p2)     # False,因为是不同对象
print(p1 == p3)     # True,因为是同一个对象
print(p1 is p2)     # False
print(p1 is p3)     # True

运行结果如下。

<__main__.Point object at 0x0000013D3EDA6E40>
<__main__.Point object at 0x0000013D3EED4E10>
<__main__.Point object at 0x0000013D3EDA6E40>
False
True
False
True

分析:

在这个例子中,我们定义了一个名为 “Point” 的类。不过在该类中,我们并没有显式定义 __eq__() 方法。

对于 p1 和 p2 来说,虽然它们的属性值完全相同,但由于这两个是独立的 Point 对象(内存地址不同),因此 p1 == p2 会返回 False。而对于 p1 和 p3 来说,它们其实是同一个对象,因此 p1 == p3 回返回 True。

从这个例子也可以看出,Python 的 “==” 是基于它们的内存地址来进行比较的。

示例 2:自定义 __eq__() 方法

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        # 如果 x 和 y 属性都相同,则两个 Point 对象相等。
        if not isinstance(other, Point):
            return NotImplemented     # 确保只与 Point 实例比较
        return self.x == other.x and self.y == other.y

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)

print(p1)
print(p2)
print(p3)

print(p1 == p2)       # True
print(p1 == p3)       # False

print(p1 is p2)       # False (不同对象)
print(p1 == (1, 2))   # False

运行结果如下。

Point(1, 2)
Point(1, 2)
Point(3, 4)
True
False
False
False

分析:

在这个例子中,我们在 Point 类中自定义实现 __eq__() 方法,使得两个 Point 对象在它们的 x 和 y 属性相同时被认为是相等的,即使它们是不同的内存对象。

示例 3:实现 __eq__() 的同时,也应实现 __hash__()

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        # 如果 x 和 y 属性都相同,则两个 Point 对象相等。
        if not isinstance(other, Point):
            return NotImplemented     # 确保只与 Point 实例比较
        return self.x == other.x and self.y == other.y

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)

print(p1)
print(p2)
print(p3)

print(p1 == p2)       # True
print(p1 == p3)       # False

print(p1 is p2)       # False (不同对象)
print(p1 == (1, 2))   # False

运行结果如下。

Point(1, 2)
Point(1, 2)
Point(3, 4)
True
False
False
False

分析:

在 Item 类中,我们只定义了 __eq__() 方法,使得 item1 == item2 为 True(因为 id 属性相同)。

不过当尝试将 Item 类的实例放入 set 或作为 dict 的键时,Python 会抛出 TypeError 异常。这是因为可哈希的对象必须满足一个条件:如果 a == b 为 True,那么 hash(a) 必须等于 hash(b)

在默认情况下,可变对象是不可哈希的。如果自定义了 __eq__(),Python 并不会自动提供默认的 __hash__()。因此,我们需要手动实现 __hash__()。

上一篇: __contains__()

下一篇: __ne__()

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号