Detailed Explanation of __eq__ Method Invocation Order and Handling Mechanism in Python

Dec 03, 2025 · Programming · 9 views · 7.8

Keywords: Python | _eq__ method | comparison operators | invocation order | NotImplemented | subclass priority

Abstract: This article provides an in-depth exploration of the handling mechanism of the equality comparison operator == in Python, focusing on the invocation order of the __eq__ method. By analyzing the official decision tree and combining specific code examples, it explains in detail how Python decides which class's __eq__ method to call in the absence of left/right versions of comparison operators. The article covers differences between Python 2.x and Python 3.x, including the role of NotImplemented return values, the subclass priority principle, and the final identity comparison fallback mechanism.

Basic Mechanism of Equality Comparison in Python

In Python, the handling mechanism of the equality comparison operator == follows a specific decision tree. When executing a == b, Python does not simply call a.__eq__(b); instead, it attempts different comparison strategies in a specific order based on object types and method implementations.

Invocation Order of the __eq__ Method

According to Python's official implementation, the invocation order of the __eq__ method is as follows:

  1. If the type of b is a strict subclass (not the same type) of the type of a, and the subclass overrides the __eq__ method, then b.__eq__(a) is called first.
  2. If condition 1 is not met, and the type of a overrides the __eq__ method (i.e., it is not object.__eq__), then a.__eq__(b) is called.
  3. If the first two steps do not successfully invoke a method, and the type of b overrides the __eq__ method, then b.__eq__(a) is called.
  4. If all the above attempts return NotImplemented, identity comparison is finally performed, equivalent to a is b.

Role of the NotImplemented Return Value

NotImplemented is a special singleton object used to indicate that an operation is not implemented for a given type. When the __eq__ method returns NotImplemented, Python treats it as if the method does not exist and continues to try the next option in the decision tree. This mechanism allows both objects to have an opportunity to participate in the comparison, enabling more flexible type interactions.

Analysis of Code Examples

Consider the following example code:

class A(object):
    def __eq__(self, other):
        print("A __eq__ called")
        return self.value == other.value

class B(object):
    def __eq__(self, other):
        print("B __eq__ called")
        return self.value == other.value

a = A()
a.value = 3
b = B()
b.value = 4
result = a == b

In this example, since A and B have no inheritance relationship, Python first attempts A.__eq__. If the implementation of A.__eq__ involves comparison with an instance of B, and the B type does not know how to compare with the A type, it may trigger a call to B.__eq__.

Subclass Priority Principle

When comparisons involve inheritance relationships, Python prioritizes the subclass's __eq__ method. For example:

class A:
    value = 3
    def __eq__(self, other):
        print('A __eq__ called')
        return self.value == other.value

class B(A):
    value = 4
    def __eq__(self, other):
        print('B __eq__ called')
        return self.value == other.value

a, b = A(), B()
result = a == b

In this example, since B is a subclass of A and overrides the __eq__ method, Python prioritizes calling B.__eq__, outputting "B __eq__ called" and then returning False.

Differences Between Python 2.x and Python 3.x

In Python 2.x, the comparison mechanism also includes support for the __cmp__ method. When the __eq__ method does not exist, Python looks for the __cmp__ method. If __cmp__ returns 0, the objects are considered equal. However, __cmp__ has been removed in Python 3.x, simplifying the comparison logic.

C-Level Implementation Details

In the CPython implementation, the comparison logic for __eq__ is primarily implemented in the C-level function do_richcompare. This function first checks whether the type of the second object is a subclass of the type of the first object and whether it overrides tp_richcompare (corresponding to __eq__ at the Python level). If so, the subclass method is called first.

The default implementation of object.__eq__ returns True if and only if the two objects are the same object (self is other); otherwise, it returns NotImplemented. This ensures that when a custom class does not override __eq__, the comparison falls back to identity comparison.

Practical Application Recommendations

When implementing the __eq__ method for custom classes, it is recommended to:

  1. Always consider type checking to ensure that the compared object types are compatible.
  2. Return NotImplemented instead of False when encountering unsupported types, to allow the other object to attempt the comparison.
  3. When overriding __eq__ in subclasses, pay attention to calling the parent class method or properly handling inherited comparison logic.
  4. Ensure that the implementation of __eq__ is consistent with __hash__, especially when objects are used as dictionary keys or set elements.

Conclusion

Python's equality comparison mechanism, through a carefully designed decision tree, balances flexibility, efficiency, and polymorphism. Understanding the invocation order of __eq__, the role of NotImplemented, and the subclass priority principle is crucial for writing correct and efficient comparison logic for custom classes. This mechanism not only ensures type safety but also supports rich type interaction patterns, reflecting the powerful expressive capabilities of Python as a dynamic language.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.