this is my code:
class Person:
def __init__(self, id):
self.id = id
def __eq__(self, other: 'Person') -> bool:
return self.id == other.id
def compare(self, other: 'Person') -> bool:
return self.id == other.id
mypy throw error: Argument 1 of "__eq__" incompatible with supertype "object"
.
But if I remove __eq__
method, mypy won't complain it though compare
is same as __eq__
, what should I do?
The root problem is that the __eq__
method is supposed to accept any object: doing my_object == 3
is legal at runtime, and should always return False. You can see this for yourself by checking the baseline type definition for object
in Typeshed: the signature of __eq__
is given as def __eq__(self, o: object) -> bool: ...
So, in order to make this work, the correct way of implementing __eq__
would be to do the following:
def __eq__(self, other: object) -> bool:
if not isinstance(other, Person):
# If we return NotImplemented, Python will automatically try
# running other.__eq__(self), in case 'other' knows what to do with
# Person objects.
return NotImplemented
return self.id == other.id
And in fact, if you update the version of mypy you're using, it'll print out a note recommending you structure your code in this way.
However, the problem with this approach is that mypy will now no longer complain if you do something silly like Person() == 3
. Technically, that ought to return a bool, but pragmatically, your code probably has a bug if you're comparing a person object against an int.
Thankfully, mypy very recently acquired a feature that can flag these sorts of errors: --strict-equality
. Now, when you run mypy with that flag, doing Person() == 3
will make mypy output errors like Non-overlapping equality check (left operand type: "Person", right operand type: "int")
even if you define __eq__
in the way described above.
Note that you'll need to use the latest version of mypy from master to use this flag until the next version of mypy (0.680) is released. That should happen in roughly 2 to 3 weeks as of time of writing.
If defining the __eq__
in the manner described above is not something you can do for whatever reason, I would personally recommend suppressing the type error instead of replacing Person with Any
.
So basically, do this:
def __eq__(self, other: 'Person') -> bool: # type: ignore
return self.id == other.id
...maybe along with an brief note of why you're suppressing the error.
The rationale here is that this definition of __eq__
strictly speaking is unsafe (it violates something known as the Liskov substitution principle) -- and if you need to do something unsafe, it's probably better to explicitly mark that you're subverting the type system rather then hiding it by using Any.
And at least this way, you can still make expressions like Person() == 3
be a type error -- if you use Any
, expressions like Person() == 3
will silently type-check. At that point, you might as well just use object
and structure your code to behave correctly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With