Issue
When implementing a comparison method between two classes the result is not as expected.
class A:
def __init__(self, name: str) -> None:
self.name = name
def __eq__(self, other: object) -> bool:
if not isinstance(other, A):
return NotImplemented
return self.name == other.name
class B(A):
def __init__(self, name: str, value: int) -> None:
super().__init__(name)
self.value = value
def __eq__(self, other: object) -> bool:
if not isinstance(other, B):
return NotImplemented
return self.name == other.name and self.value == other.value
a = A("a")
b = B("a", 1)
print(a == b) # True
print(b == a) # True
What I want is that different instances of these classes with different properties are not equal, so I'd expect False in both cases.
The reason that we get True instead is because we return NotImplemented in the __eq__ method of B if the other object is not an instance of B. This causes the __eq__ method of A to be called. As B is a subclass of A, isinstance(b, A) returns True.
Solution (Try 1)
Instead of checking using isinstance(other, A) we can use type(other) is A:
class A:
def __init__(self, name: str) -> None:
self.name = name
def __eq__(self, other: object) -> bool:
if not type(other) is A:
return NotImplemented
return self.name == other.name
class B(A):
def __init__(self, name: str, value: int) -> None:
super().__init__(name)
self.value = value
def __eq__(self, other: object) -> bool:
if not type(other) is B:
return NotImplemented
return self.name == other.name and self.value == other.value
a = A("a")
b = B("a", 1)
print(a == b) # False
print(b == a) # False
This works as expected, however mypy does not like it:
error: Returning Any from function declared to return "bool" [no-any-return]
error: "object" has no attribute "name" [attr-defined]
error: Returning Any from function declared to return "bool" [no-any-return]
error: "object" has no attribute "name" [attr-defined]
error: "object" has no attribute "value" [attr-defined]
Solution (Try 2)
As far as I could figure out, in order for mypy to like the solution, the isinstance checks have to stay. So I added the type check to the value comparison.
class A:
def __init__(self, name: str) -> None:
self.name = name
def __eq__(self, other: object) -> bool:
if not isinstance(other, A):
return NotImplemented
return self.name == other.name and type(other) is A
class B(A):
def __init__(self, name: str, value: int) -> None:
super().__init__(name)
self.value = value
def __eq__(self, other: object) -> bool:
if not isinstance(other, B):
return NotImplemented
return self.name == other.name and self.value == other.value and type(other) is B
a = A("a")
b = B("a", 1)
print(a == b) # False
print(b == a) # False
Now mypy is fine with the code, but pylint still doesn't like the solution:
C0123: Using type() instead of isinstance() for a typecheck. (unidiomatic-typecheck)
How can I resolve this issue correctly?