Типизация на Python: лучший способ аннотировать родительский класс, возвращающий объект типа подкласса

#python #mypy #python-typing

Вопрос:

У меня есть базовый класс, такой как follow:

 class BaseClass:
    @classmethod
    def create(cls, arg1: str) -> BaseClass:
        instance = cls(arg1)
        return instance
 

И производный класс, подобный этому:

 class DerivedClass(BaseClass):
    def __init__(self, arg: str) -> None:
        self.arg = arg 
 

Теперь, когда я это делаю

 data: DerivedClass = DerivedClass.create('first arg)
 

mypy выдает следующую ошибку:

 "BaseClass" cannot be assigned to declared type "DerivedClass"
"BaseClass" is incompatible with "DerivedClass"
 

Как я решаю эту проблему, используя typing.cast

 data: DerivedClass = cast(
    DerivedClass, 
    DerivedClass.get(derived_class_instance)
)
 

Как я должен решить эту проблему без использования typing.cast

Ответ №1:

Вы можете перейти BaseClass на это:

 T = TypeVar("T")

from typing import TypeVar, Type

class BaseClass:
    @classmethod
    def get(cls: Type[T], instance: T) -> T:
        return instance
 

Это заставляет instance быть того типа, для которого get вызывается метод класса. Поэтому BaseClass.get берет a BaseClass и возвращает a BaseClass , в то время DerivedClass.get как берет a DerivedClass и возвращает a DerivedClass . Запуск mypy приводит к:

 Success: no issues found in 1 source file
 

Комментарии:

1. Большое спасибо, это прекрасно работает. но когда я использую cls для создания экземпляра в classmethod, он говорит BaseClass cannot be instantiated . Пожалуйста, проверьте правки. Извините, что изменил вопрос.

2. В таком случае mypy это правильно. Вы предполагаете, что все возможное cls может быть создано с помощью a str . Но только DerivedClass может и BaseClass не может, поэтому BaseClass.create("abc") произошла бы ошибка во время выполнения. Вы понимаете, почему mypy отклоняет вашу программу? @wrufesh

3. Когда cls другие свойства, к которым мы пытаемся получить доступ в базовом классе, чем мы получаем ошибку, как xyz атрибут не определен. в противном случае я сейчас не получаю никаких ошибок. также добавление T = TypeVar("T", bound=BaseClass) также решает проблему атрибутов.