Search code examples
pythonpython-typingmypy

Why does defining the argument types for __eq__ throw a MyPy type error?


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 already have argument types 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.


Solution

  • == 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 accepts this code as-is.

    On Python 3.10 and up, you can use types.NotImplementedType to be more explicit about the NotImplemented possibility:

    from types import NotImplementedType
    
    class MyObject(object):
        def __init__(self, value: int=5) -> None:
            self.value = value
    
        def __eq__(self, other: object) -> bool | NotImplementedType:
            if not isinstance(other, MyObject):
                return NotImplemented
            return self.value == other.value
    

    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.