I'm using Python 3.5.1 and the newly released MyPy v0.4.1 static type analyzer.
I have some more complex code that I've reduced down to this simplest possible python class needed to reproduce the error:
class MyObject(object):
def __init__(self, value: int=5) -> None:
self.value = value
def __eq__(self, other: MyObject) -> bool:
return self.value == other.value
Running the type checker mypy test.py
produces the following error:
test.py: note: In class "MyObject":
test.py:5: error: Argument 1 of "__eq__" incompatible with supertype "object"
My theory based on these docs is that __eq__
and __ne__
on object have types already defined, which are clashing with my subclass's redefinition of these types. My question is how to I define these types to make sure __eq__
is type-checked with my chosen type.
Silencing errors based on error codes You can use a special comment # type: ignore[code, ...] to only ignore errors with a specific error code (or codes) on a particular line. This can be used even if you have not configured mypy to show error codes.
“Mypy is an optional static type checker for Python that aims to combine the benefits of dynamic (or 'duck') typing and static typing. Mypy combines the expressive power and convenience of Python with a powerful type system and compile-time type checking.” A little background on the Mypy project.
Spurious errors and locally silencing the checker You can add a # type: ignore comment to tell mypy to ignore this error: import frobnicate # type: ignore frobnicate. start() # Okay! The second line is now fine, since the ignore comment causes the name frobnicate to get an implicit Any type.
Type hints improve IDEs and linters. They make it much easier to statically reason about your code. Type hints help you build and maintain a cleaner architecture. The act of writing type hints forces you to think about the types in your program.
==
is supposed to take arbitrary other objects, not just objects of your type. If it doesn't recognize the other object, it should return NotImplemented
:
class MyObject(object):
def __init__(self, value: int=5) -> None:
self.value = value
def __eq__(self, other: object) -> bool:
if not isinstance(other, MyObject):
return NotImplemented
return self.value == other.value
NotImplemented
isn't an instance of bool
, but mypy seems to have a weird special case for that. It wants the return annotation to be bool
, and it doesn't complain about the return NotImplemented
line.
Also, if you need to refer to MyObject
for type hints inside its own body, you need to use a string, 'MyObject'
instead of MyObject
. MyObject
doesn't exist yet.
Your reading of the docs is right -- you need to give the method (__eq__
) the same signature as it has already in the base class (object
), or else a more permissive one.
The reason for that is that because your MyObject
is a subtype of object
, a MyObject
could be passed anywhere that expects an object
... which means that that code could compare it with any other object
, and there's no legitimate way for the type checker to complain. So, to reflect that, your __eq__
has to be written to expect any object
.
What you can do is right up front in the method's body, check the type and return (or raise an exception):
if not isinstance(other, MyObject):
return False
Then as those docs say, Mypy is smart enough that after that check, it will know that other
is a MyObject
and treat it accordingly.
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