Martin Thoma
Martin Thoma

Reputation: 136645

How do I make a type annotation for a list of subclass instances, e.g to concatenate two lists?

I want to iterate over List[A] and List[Subclass of A] and do the same loop. The best way I can see to do that is to concatenate the two lists. However, mypy is not happy about it.

How can I concatenate the two and keep mypy happy?

Currently, I do # type: ignore[operator]. I would like to avoid that, if possible.

MVCE

# Core Library modules
from typing import Iterable

# Third party modules
from pydantic import BaseModel


class Animal(BaseModel):
    height: float
    weight: float


class Cat(Animal):
    lives: int = 7


cats = [Cat(height=1, weight=2, lives=7), Cat(height=3, weight=2, lives=1)]
animals = [Animal(height=9, weight=9)]

combined: Iterable[Animal] = cats + animals

for animal in combined:
    print(animal)

gives

$ mypy untitled.py
untitled.py:20: error: Unsupported operand types for + ("List[Cat]" and "List[Animal]")
Found 1 error in 1 file (checked 1 source file)

Upvotes: 1

Views: 420

Answers (2)

Kroshka Kartoshka
Kroshka Kartoshka

Reputation: 1178

If it doesn't bother you, change List to Sequence

from typing import Sequence

class Base: pass

class Derived(Base): pass

ds: Sequence[Derived] = [Derived()]
bs: Sequence[Base] = ds

with which you'll get

$ mypy temp.py
Success: no issues found in 1 source file

Upvotes: 0

alex_noname
alex_noname

Reputation: 32233

This situation occurs because list is invariant (provides an illustrative example).

I can offer two solutions:

  1. Explicitly define both lists as List[Animal] for successful concatenation:
cats: List[Animal] = [Cat(height=1, weight=2, lives=7), Cat(height=3, weight=2, lives=1)]
animals: List[Animal] = [Animal(height=9, weight=9)]
combined: Iterable[Animal] = cats + animals

for animal in combined:
    print(animal)
  1. Use itertools.chain for consecutive iteration:
cats = [Cat(height=1, weight=2, lives=7), Cat(height=3, weight=2, lives=1)]
animals = [Animal(height=9, weight=9)]

for animal in itertools.chain(cats, animals):
    print(animal)

Upvotes: 2

Related Questions