Python: 'super' object has no attribute '__eq__' Error When Overriding __eq__ – Why It Happens and How to Fix

In Python, the __eq__ method is crucial for defining custom equality checks between objects. By overriding __eq__, you can specify how instances of your class should be compared using the == operator. However, a common and perplexing error many developers encounter is: AttributeError: 'super' object has no attribute '__eq__'

This error occurs when attempting to call super().__eq__(other) in a subclass’s __eq__ method, but the super proxy object cannot find the __eq__ attribute in the parent class hierarchy. In this blog, we’ll demystify why this error happens, explore common scenarios that trigger it, and provide step-by-step solutions to fix it. Whether you’re new to Python or a seasoned developer, this guide will help you master __eq__ overrides and avoid pitfalls with super().

Table of Contents#

  1. Understanding the __eq__ Method
  2. The super Object and Attribute Lookup
  3. Why the Error Occurs: Common Scenarios
  4. How to Fix the Error: Step-by-Step Solutions
  5. Advanced Considerations
  6. Conclusion
  7. References

Understanding the __eq__ Method#

The __eq__ method in Python defines how instances of a class are compared for equality using the == operator. By default, all classes inherit __eq__ from the base object class, which checks for identity (i.e., a == b returns True only if a is b).

For example:

class Person:
    def __init__(self, name):
        self.name = name
 
# Default __eq__ checks identity
p1 = Person("Alice")
p2 = Person("Alice")
print(p1 == p2)  # False (different objects in memory)
print(p1 is p2)  # False (same as above)

To customize equality (e.g., compare attributes like name), override __eq__:

class Person:
    def __init__(self, name):
        self.name = name
 
    def __eq__(self, other):
        if not isinstance(other, Person):
            return False  # Compare only to Person instances
        return self.name == other.name  # Check name attribute
 
p1 = Person("Alice")
p2 = Person("Alice")
print(p1 == p2)  # True (same name)

While overriding __eq__ is powerful, errors arise when combining it with super(), which delegates to parent classes. Let’s explore why.

The super Object and Attribute Lookup#

The super() function returns a proxy object that delegates method calls to the next class in the Method Resolution Order (MRO). The MRO is a sequence that determines the order in which Python searches for methods in a class hierarchy (especially critical for multiple inheritance).

For example, in a simple hierarchy:

class Parent:
    def greet(self):
        print("Parent says hello!")
 
class Child(Parent):
    def greet(self):
        print("Child says hello!")
        super().greet()  # Delegates to Parent.greet()
 
child = Child()
child.greet()
# Output:
# Child says hello!
# Parent says hello!

Here, super().greet() in Child calls Parent.greet() because Parent is next in the MRO (ChildParentobject).

Crucially: super() does not directly reference the "parent" class—it follows the MRO. If the MRO skips a class with the desired method (or the method is missing), super() will fail to find it, triggering an AttributeError.

Why the Error Occurs: Common Scenarios#

The error 'super' object has no attribute '__eq__' occurs when super().__eq__(other) is called, but the super proxy cannot find __eq__ in the MRO. Let’s break down the most common causes.

Scenario 1: Parent Class Explicitly Removes __eq__#

If a parent class explicitly sets __eq__ = None, it removes the method from the class. When a child class calls super().__eq__, the super proxy (pointing to the parent) will have no __eq__ attribute.

Example:

class Parent:
    __eq__ = None  # Explicitly remove __eq__
 
class Child(Parent):
    def __init__(self, value):
        self.value = value
 
    def __eq__(self, other):
        if not isinstance(other, Child):
            return False
        return super().__eq__(other)  # Error here!
 
child1 = Child(10)
child2 = Child(10)
print(child1 == child2)  # AttributeError: 'super' object has no attribute '__eq__'

Why it fails: Parent has __eq__ = None, so super() in Child points to Parent, which lacks __eq__.

Scenario 2: Multiple Inheritance with Broken MRO#

In multiple inheritance, if the MRO prioritizes a class that lacks __eq__, super() will fail to find the method.

Example:

class Base:
    __eq__ = None  # No __eq__
 
class Mixin:
    pass  # Inherits __eq__ from object (default identity check)
 
class MyClass(Base, Mixin):  # MRO: MyClass → Base → Mixin → object
    def __eq__(self, other):
        if not isinstance(other, MyClass):
            return False
        return super().__eq__(other)  # super() points to Base (no __eq__)
 
obj1 = MyClass()
obj2 = MyClass()
print(obj1 == obj2)  # AttributeError: 'super' object has no attribute '__eq__'

Why it fails: MyClass inherits from Base first, so the MRO is MyClassBaseMixinobject. super() in MyClass points to Base, which has __eq__ = None.

Scenario 3: Accidental Override in the Class Hierarchy#

A middle class in the hierarchy may override __eq__ but fail to delegate to its own super(), breaking the chain for subclasses.

Example:

class Grandparent:
    def __eq__(self, other):
        print("Grandparent __eq__")
        return self is other  # Default identity check
 
class Parent(Grandparent):
    def __eq__(self, other):
        return False  # Override without super()
 
class Child(Parent):
    def __eq__(self, other):
        if not isinstance(other, Child):
            return False
        return super().__eq__(other)  # Calls Parent.__eq__, which returns False unconditionally
 
child1 = Child()
child2 = Child()
print(child1 == child2)  # False (unintended behavior, but no error... yet)

Why it’s risky: While this doesn’t trigger the AttributeError, it breaks the expected behavior of super(). If Parent later removes __eq__, the error will surface.

How to Fix the Error: Step-by-Step Solutions#

Let’s resolve the error with targeted fixes for each scenario.

Fix 1: Remove __eq__ = None from Parent Classes#

If a parent class unnecessarily sets __eq__ = None, remove that line to restore the default object.__eq__ (or define a custom __eq__).

Corrected Example:

class Parent:
    pass  # Restores default object.__eq__ (no __eq__ = None)
 
class Child(Parent):
    def __init__(self, value):
        self.value = value
 
    def __eq__(self, other):
        if not isinstance(other, Child):
            return False
        return self.value == other.value and super().__eq__(other)  # Now works!
 
child1 = Child(10)
child2 = Child(10)
child3 = child1
print(child1 == child2)  # False (different instances, object.__eq__ returns False)
print(child1 == child3)  # True (same instance)

Why it works: Parent now inherits __eq__ from object, so super().__eq__ in Child calls object.__eq__ (identity check).

Fix 2: Explicitly Call object.__eq__#

If the parent class cannot be modified (e.g., it’s from a third-party library), bypass super() and directly call object.__eq__(self, other) to use the default identity check.

Example:

class Parent:
    __eq__ = None  # Cannot modify Parent
 
class Child(Parent):
    def __init__(self, value):
        self.value = value
 
    def __eq__(self, other):
        if not isinstance(other, Child):
            return False
        if self.value != other.value:
            return False
        return object.__eq__(self, other)  # Explicitly use object's __eq__
 
child1 = Child(10)
child2 = Child(10)
child3 = child1
print(child1 == child2)  # False (different instances)
print(child1 == child3)  # True (same instance)

Why it works: object.__eq__ is always available and checks identity, avoiding the broken super() chain.

Fix 3: Adjust MRO in Multiple Inheritance#

In multiple inheritance, reorder parent classes to prioritize those with __eq__ in the MRO.

Corrected Example:

class Mixin:
    pass  # Has object.__eq__
 
class Base:
    __eq__ = None
 
class MyClass(Mixin, Base):  # MRO: MyClass → Mixin → Base → object
    def __eq__(self, other):
        if not isinstance(other, MyClass):
            return False
        return super().__eq__(other)  # Now super() points to Mixin (has __eq__)
 
obj1 = MyClass()
obj2 = MyClass()
print(obj1 == obj2)  # False (different instances, no error)

Why it works: MyClass now inherits from Mixin first, so super() delegates to Mixin, which uses object.__eq__.

Fix 4: Validate other Before Delegating to super()#

Ensure other is an instance of your class (or a subclass) before calling super().__eq__. This avoids passing incompatible types to parent methods.

Example:

class Child:
    def __init__(self, value):
        self.value = value
 
    def __eq__(self, other):
        if not isinstance(other, Child):
            return False  # Reject non-Child instances early
        return self.value == other.value and super().__eq__(other)
 
child1 = Child(10)
child2 = Child(10)
print(child1 == child2)  # False (different instances)

Why it helps: Prevents super().__eq__ from being called with invalid other types (e.g., None), which could cause unrelated errors.

Advanced Considerations#

Cooperative Multiple Inheritance#

When using super() in multiple inheritance, ensure all classes in the hierarchy follow cooperative inheritance (i.e., they call super().__eq__ themselves). This ensures the MRO chain remains unbroken.

Example:

class A:
    def __eq__(self, other):
        print("A.__eq__")
        return super().__eq__(other)
 
class B:
    def __eq__(self, other):
        print("B.__eq__")
        return super().__eq__(other)
 
class C(A, B):  # MRO: C → A → B → object
    def __eq__(self, other):
        print("C.__eq__")
        return super().__eq__(other)
 
c = C()
c == c  # Output: C.__eq__ → A.__eq__ → B.__eq__ → (object.__eq__ returns True)

Consistency with __hash__#

If you override __eq__, Python disables the default __hash__ (to ensure consistency). If your objects need to be hashable (e.g., for use in dict or set), define __hash__ explicitly:

class Person:
    def __init__(self, name):
        self.name = name
 
    def __eq__(self, other):
        return isinstance(other, Person) and self.name == other.name
 
    def __hash__(self):
        return hash(self.name)  # Consistent with __eq__

Conclusion#

The 'super' object has no attribute '__eq__' error occurs when super().__eq__ cannot find __eq__ in the MRO. This typically stems from parent classes removing __eq__, broken MRO in multiple inheritance, or accidental method overrides.

To fix it:

  • Remove __eq__ = None from parent classes.
  • Explicitly call object.__eq__ if parents lack __eq__.
  • Adjust MRO in multiple inheritance to prioritize classes with __eq__.
  • Validate other before delegating to super().

By understanding MRO and super() behavior, you can write robust, maintainable equality checks in Python.

References#