This question is about how __eq__
should be implemented in a class hierarchy.
Let’s say we have the following setup:
class Person:
"""A person with a name."""
def __init__(self, name):
self.name = name
def __eq__(self, other):
if not isinstance(other, type(self)):
return NotImplemented
return self.name == other.name
def __repr__(self):
return f'Person("{self.name}")'
class Student(Person):
"""A student with a name and a student id."""
def __init__(self, name, student_id):
super().__init__(name)
self.student_id = student_id
def __repr__(self):
return f'Student("{self.name}", {self.student_id})'
The question is specifically about how should __eq__
be implemented?
The way it is written in the above code block results in:
p = Person("SomeName")
s = Student("SomeName", 1)
print(p == s) # True
As s
is an instance of a class derived from the class of instance p
, the first call is
s.__eq__(p). # NotImplemented, as Person is not the same class or a subclass of Student
Since NotImplemented
was returned, the following call is made:
p.__eq__(s) # True, as Student is a subclass of Person and the name attribute of both instances are identical
But “should” an instance of a derived class ever considered to be equal (value equality) to an instance of a parent class?
If the answer to this question is “no”, we can’t rely on the __eq__
method (as it was written above) via inheritance, but must override it in the subclass.
Maybe this is contextual and case-to-case dependant, but if one would want to allow value equality to be True
only when the types of both instances in the comparison are identical, couldn’t we write __eq__
as:
def __eq__(self, other):
if type(self) != type(other):
return NotImplemented
return self.name == other.name
As Person != Student
the ==
operator would fall back to object identity which would return False
.
What is the correct approach to the __eq__
-paradigm in this situation?
9
Why not use a more generalized type checker in your method to allow for all possible Person
derivatives?
if not isinstance(other, Person): return NotImplemented
This way, a Student
and a Person
are always a Person
instance.
1