mypy
здесь правильно, потому что ваш Parent
не реализует AbstractParent
правильно - для этого он должен определить метод foo
, который возвращает список AbstractChild
ren, а не Child
ren , Это связано с тем, что коллекции не являются полиморфными c (и это верно и для других языков, например, Java): List[AbstractChild]
не совпадает с типом List[Child]
, а List[Child]
не наследуется от List[AbstractChild]
просто потому что Child
делает. Если бы у нас не было этого ограничения, возможны ошибки, подобные этой:
class AbstractChild(ABC):
pass
class Child(AbstractChild):
pass
class GrandChild(AbstractChild):
pass
grandchildren: List[GrandChild] = [GrandChild()]
all_children: List[AbstractChild] = grandchildren
all_children.append(Child())
grandchild: GrandChild = grandchildren[0] # would pass typechecks but is a Child, actually
(это перефразированный пример ответа Джона Скита на аналогичный вопрос в Java) .
Java, например, перехватывает этот тип ошибок при компиляции и требует явной ковариации, например, List<? extends Child>
для чтения и List<? super Child>
для записи в список.
In В вашем случае вы бы также указали тип c. В приведенном ниже примере я изменяю AbstractParent
, чтобы возвращать List
элементов того же типа C
, которые могут быть любыми подклассами AbstractChild
, а Parent
является конкретной реализацией обобщенного c AbstractChild
с конкретным дочерним типом Child
:
from typing import List, TypeVar, Generic
C = TypeVar('C', bound='AbstractChild')
class AbstractParent(ABC, Generic[C]):
@abstractmethod
def foo(self) -> List[C]: pass
class Parent(AbstractParent["Child"]):
def foo(self) -> List["Child"]:
return []
Дополнительные примеры можно найти в главе Generics из mypy
документов, в частности, Variance of generi c типы секция.