Search code examples
pythonmypypydantic

Type Incompatibility Issue with Pydantic Model Inheritance and List of Subclasses


When inheriting from a Pydantic model and attempting to override a parameter with a list of a subclass, mypy throws a type error. The base class defines the parameter as a list of the parent class, but when trying to override it with a list of the subclass, mypy sees it as incompatible.

Example that fails:

from pydantic import BaseModel

class Name(BaseModel):
    data: str

class EnglishName(Name):
    data: str

class Animal(BaseModel):
    name: list[Name]

class AmericaAnimal(Animal):
    name: list[EnglishName]  # Incompatible types in assignment (expression has type "list[EnglishName]", base class "Animal" defined the type as "list[Name]")

In contrast, the following example passes:

from pydantic import BaseModel

class Name(BaseModel):
    data: str

class EnglishName(Name):
    data: str

class Animal(BaseModel):
    name: Name

class AmericaAnimal(Animal):
    name: EnglishName

Given that EnglishName inherits from Name, the type should be compatible when using a list, but this is not the case with mypy when overriding with a list of the subclass.

I tried defining a base Pydantic model Name with a data field of type str. Then, I created a subclass EnglishName inheriting from Name. Next, I defined another base Pydantic model Animal with a field name as a list of Name objects. However, when I tried to override this field in a subclass AmericaAnimal to be a list of EnglishName objects, I expected the type to be compatible since EnglishName is a subclass of Name. Instead, I encountered a type incompatibility error from mypy.

Expected Behavior: I expected AmericaAnimal to accept a list of EnglishName objects as its name field due to the inheritance relationship between Name and EnglishName.

Actual Behavior: Mypy raised an "Incompatible types in assignment" error, indicating a type mismatch between the name field of Animal and the overridden name field of AmericaAnimal.


Solution

  • TL;DR: use typing.Sequence instead of list.

    Running mypy on your code gives the following descriptive error:

    $ mypy animal.py
    animal.py:13: error: Incompatible types in assignment (expression has type "list[EnglishName]", base class "Animal" defined the type as "list[Name]")  [assignment]
    animal.py:13: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
    animal.py:13: note: Consider using "Sequence" instead, which is covariant
    Found 1 error in 1 file (checked 1 source file)
    

    The problem comes down to covariance, as explained in this answer. Also have a look at the linked documentation for some suggestions.