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#
- Understanding the
__eq__Method - The
superObject and Attribute Lookup - Why the Error Occurs: Common Scenarios
- How to Fix the Error: Step-by-Step Solutions
- Advanced Considerations
- Conclusion
- 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 (Child → Parent → object).
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 MyClass → Base → Mixin → object. 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__ = Nonefrom parent classes. - Explicitly call
object.__eq__if parents lack__eq__. - Adjust MRO in multiple inheritance to prioritize classes with
__eq__. - Validate
otherbefore delegating tosuper().
By understanding MRO and super() behavior, you can write robust, maintainable equality checks in Python.
References#
- Python Documentation:
super() - Python Documentation:
__eq__Method - Python MRO (Method Resolution Order)
- PEP 253: Making Types Look More Like Classes (New-style classes and MRO)
- PEP 367: New Super (Enhancements to
super())