partha biswas
partha biswas

Reputation: 242

subclassing of NamedTuple gives incompatible return type/ argument type

Consider the following code snippet:

class A_TUPLE(NamedTuple):
    ...

class Animal:
    def get(self) -> Sequence[NamedTuple]:
        ...

    def get_one(self, x: Sequence[NamedTuple]) -> None:
        ...

class Dog(Animal):
    def get(self) -> Sequence[A_TUPLE]:  # error: Return type "Sequence[A_TUPLE]" of "get" incompatible with return type "Sequence[NamedTuple]" in supertype "Animal"
        ...
    def get_one(self, x: Sequence[A_TUPLE]) -> None:  # error: Argument 1 of "get_one" is incompatible with supertype "Animal"; supertype defines the argument type as "Sequence[NamedTuple]"
        ...

Running mypy on the above code snippet gives error as shown in the comments.

As per mypy - invariance this should be possible.

Upvotes: 2

Views: 351

Answers (1)

partha biswas
partha biswas

Reputation: 242

Sequence[NamedTuple] and Sequence[A_TUPLE] can only be compatible if the names/types of NamedTuple is known in advance. Otherwise, with just a declaration of NamedTuple it is not possible to validate if the return type of the derived class is compatible or type safe with the base class.

e.g. This will work:

class A_TUPLE(NamedTuple):
    a: str
    b: int

class Animal:
    def get(self) -> Sequence[Tuple[str, int]]:
        ...

class Dog(Animal):
    def get(self) -> Sequence[A_TUPLE]:  # okay!
        ...

The second function def get_one(self, x: Sequence[A_TUPLE]) has a different issue. The base class version says get_one() can take any tuple, while the derived version requires the specific type A_TUPLE. But if you have a Dog, you can treat it as an Animal. So Dog must accept everything that Animal accepts, which isn’t the case here.

Upvotes: 2

Related Questions