@ilya_chch

Как описать Generic класс?

есть базовый класс:
PayloadT = TypeVar('PayloadT', bound=Payload)
ParamsT = TypeVar('ParamsT', bound=Params)

class BaseService(Generic[PayloadT, ParamsT]):
    payload_class: Optional[Type[PayloadT]] = None
    params_class: Optional[Type[ParamsT]] = None

    def __init__(self, payload: Optional[PayloadT] = None, params: Optional[ParamsT] = None) -> None:
        self.payload = payload
        self.params = params

    def do_something(self) -> None:
        raise NotImplementedError()


и есть несколько его наследников:
class ServicePayload(Payload):
    some_string: str

class ServiceParams(Params):
    some_int: int

class Service1(BaseService[ServicePayload, None]):
    payload_class: Type[ServicePayload] = ServicePayload

    def do_something(self) -> None:
        print(self.payload.some_string)  # например

class Service2(BaseService[None, ServiceParams]):
    params_class: Type[ServiceParams] = ServiceParams

    def do_something(self) -> None:
        print(self.params.some_int)  # например

class Service3(BaseService[ServicePayload, ServiceParams]):
    payload_class: Type[ServicePayload] = ServicePayload
    params_class: Type[ServiceParams] = ServiceParams

    def do_something(self) -> None:
        print(self.payload.some_string)  # например
        print(self.params.some_int)  # например


при описании методов в классах наследниках, mypy выдает ошибку на строках, где я пытаюсь что-то сделать с атрибутами ServicePayload и ServiceParams:
...error: Item "None" of "Optional[Any]" has no attribute "some_string"
...error: Item "None" of "Optional[Any]" has no attribute "some_int"


Как описать type hints для подобной ситуации?

подразумевается, что BaseService не будет использоваться напрямую, только через наследников
  • Вопрос задан
  • 103 просмотра
Пригласить эксперта
Ответы на вопрос 1
@ilya_chch Автор вопроса
эксперименты показали, что, как вариант, наследников можно описывать так:

class Service1(BaseService[ServicePayload, None]):
    payload_class = ServicePayload
    payload: ServicePayload

    def do_something(self) -> None:
        print(self.payload.some_string)  # например

class Service2(BaseService[None, ServiceParams]):
    params_class = ServiceParams
    params: ServiceParams

    def do_something(self) -> None:
        print(self.params.some_int)  # например

class Service3(BaseService[ServicePayload, ServiceParams]):
    payload_class = ServicePayload
    payload: ServicePayload
    params_class = ServiceParams
    params: ServiceParams

    def do_something(self) -> None:
        print(self.payload.some_string)  # например
        print(self.params.some_int)  # например


но, возможно, есть более "правильный" это сделать?
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы