小能豆

Python 3.11 Typing for multiple inheritance models

py

I want to implement typing for a group of related classes that share a common metaclass (AMeta), there are two ‘de facto’ abstract parent classes:

  • A (:type[AMeta])
  • ADerived - that also inherits from another class C.

Finally there are actual implementation models of ADerived (D1, D2, … these are dynamically created) and A (E, F, …) parent classes.

The implementations of A (E, F) have also a class variable of type ADerived (_DerivedModel) and the problem I have is trying to make mypy infer the correct type for them.

I know this piece of code might not make sense, but the actual implementation is more complex and the derived models are dynamically created. Here’s a simplification of it:

from __future__ import annotations
from typing import TypeVar, Type, ClassVar

_BModel = TypeVar("_BModel", bound="ADerived")


class C:
    pass


class AMeta(type):
    @property
    def BModel(cls: Type[A]) -> Type[_BModel]:
        return cls._DerivedModel


# Abstract Models
class A(metaclass=AMeta):
    _DerivedModel: ClassVar[Type[_BModel]]


class ADerived(A, C):
    pass


# Derived Models (this models are dynamically created)
class D1(ADerived):
    pass


class D2(ADerived):
    pass


# Implementations


class E(A):
    _DerivedModel = D1


class F(A):
    _DerivedModel = D2


MyDerived1 = E.BModel  # Should be infered by mypy as type[D1]
MyDerived2 = F.BModel  # Should be infered by mypy as type[D2]

阅读 77

收藏
2023-12-12

共1个答案

小能豆

In this case, you can use the Type function from the typing module to explicitly annotate the type of _DerivedModel in the parent classes E and F. This will help mypy infer the correct types for MyDerived1 and MyDerived2.

Here’s the modified code:

from __future__ import annotations
from typing import TypeVar, Type, ClassVar

_BModel = TypeVar("_BModel", bound="ADerived")


class C:
    pass


class AMeta(type):
    @property
    def BModel(cls: Type[A]) -> Type[_BModel]:
        return cls._DerivedModel


# Abstract Models
class A(metaclass=AMeta):
    _DerivedModel: ClassVar[Type[_BModel]]


class ADerived(A, C):
    pass


# Derived Models (these models are dynamically created)
class D1(ADerived):
    pass


class D2(ADerived):
    pass


# Implementations
class E(A):
    _DerivedModel: ClassVar[Type[D1]] = D1


class F(A):
    _DerivedModel: ClassVar[Type[D2]] = D2


MyDerived1: Type[D1] = E.BModel
MyDerived2: Type[D2] = F.BModel

By explicitly annotating _DerivedModel with ClassVar[Type[D1]] and ClassVar[Type[D2]] in classes E and F respectively, you guide mypy in inferring the correct types for MyDerived1 and MyDerived2.

2023-12-12