Use list of derived class as list of base class in Python

Question:

I have a function which takes a list of a base class as argument, and I have a variable which is a list of a derived class. Using this variable as the argument gives mypy error: Argument 1 to "do_stuff" has incompatible type "List[DerivedClass]"; expected "List[BaseClass]".

class BaseClass(TypedDict):
    base_field: str

class DerivedClass(BaseClass):
    derived_field: str

def do_stuff(data: List[BaseClass]) -> None:
    pass

foo: List[DerivedClass] = [{'base_field': 'foo', 'derived_field': 'bar'}]

do_stuff(foo)

If the argument and variable are instead BaseClass and DerivedClass respectively, i.e. not lists, it understands that the variable can be casted implicitly to the base class. But for lists it doesn’t work. How can I solve this, preferably other than #type: ignore.

Answers:

It depends on what exactly do_stuff is doing, but nine times out of ten the best solution is to use Sequence instead of List:

from typing import Sequence

def do_stuff(data: Sequence[BaseClass]) -> None:
    pass

The reason you can’t use List[BaseClass] here is that do_stuff would be allowed to add BaseClass instances to data, which would in turn break foo in the caller. Sequence doesn’t imply mutability, so do_stuff is not allowed (static-typing-wise) to modify a Sequence parameter, which prevents that issue. (Put differently, Sequence is covariant and List is invariant. Most mutable generics are invariant because of exactly this issue.)

If do_stuff does need to mutate data, you’ll need to rethink the typing — should it be allowed to add a BaseClass to it? If not, maybe do_stuff should take a List[DerivedClass]. If so, you need to declare foo as a List[BaseClass] to account for that possibility.

Answered By: Samwise

You can try using a TypeVar and bound it to BaseClass. In code, that looks like

from typing import TypeVar
T = TypeVar('T', bound=BaseClass)

def do_stuff(data: list[T]) -> None:
    pass

this also makes mypy happy

Answered By: Simon Hawe
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.