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?
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