Search code examples
pythonpycharmpylintpython-typing

In python, can I narrow down protocol-defined properties of the class?


Let me explain with an example:

import typing as t
from dataclasses import dataclass

# describe our protocols
class ID(t.Protocol):
    "This protocol describes some kind of ID."
    def __hash__(self) -> int:
        raise NotImplementedError
    def __eq__(self, other) -> bool:
        raise NotImplementedError
    def __str__(self) -> str:
        raise NotImplementedError

class ObjectWithID(t.Protocol):
    "This protocol describes an object that has an ID."
    @property
    def id(self) -> ID:
        raise NotImplementedError

# now describe real classes
class MyID(ID):
    "Implementation of ID protocol"
    def __init__(self, val: int):
        self.value: int = val
    def __hash__(self) -> int:
        return self.value
    def __eq__(self, other) -> bool:
        return isinstance(other, MyID) and self.value == other.value
    def __str__(self) -> str:
        return str(self.value)

@dataclass
class MyObject(ObjectWithID):
    id: MyID # <<< THIS

Now, PyCharm insists that id being of type MyID breaks compatibility with ObjectWithID protocol. pylint, however, doesn't mind this line. Neither do I, because from my point of view any correct instance of MyObject will be compatible with ObjectWithID protocol.

Who is correct here? Is it safe for me to just suppress the warning in PyCharm?

I can see that if there would exist another implementation of ID, MyObject wouldn't accept it, where as ObjectWithID could. But is that really such a problem?


Solution

  • As MyID inherit from ID I think pycharm is wrong and pylint is right.

    mypy says the following regarding subclasses in its documentation:

    Any instance of a subclass is also compatible with all superclasses

    See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#class-types