Python проблем с аннотациями типов с наследованием типов и перегрузкой членов - PullRequest
1 голос
/ 18 июня 2020

См. Пример ниже с python 3.7, где я не могу найти способ правильно аннотировать. Ошибки аннотаций показаны в комментариях и предоставляются mypy.

  • У меня есть «generi c class», который реализует «generi c members». И конкретные классы и члены, наследующие эту структуру.
  • Конкретные члены могут иметь дополнительные методы и использовать разные аргументы для конструктора.

Как правильно аннотировать это?

Большое спасибо.

import abc
import typing


class ParentProvider(metaclass=abc.ABCMeta):
    def __init__(self) -> None:
        pass


class ChildProvider(ParentProvider):
    def __init__(self, who: str) -> None:
        ParentProvider.__init__(self)
        self._who: str = who

    @property
    def p(self) -> str:
        return "Hello %s" % self._who


class Parent(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def providerType(self) -> typing.Type[ParentProvider]:
        pass

    @property
    @abc.abstractmethod
    def providerKwargs(self) -> typing.Dict[str, typing.Any]:
        pass

    @property
    def provider(self) -> ParentProvider:
        # How to avoid the following error?
        # error: Too many arguments for "ParentProvider"  [call-arg]
        return self.providerType(**self.providerKwargs)

    @abc.abstractmethod
    def useIt(self) -> None:
        pass


class Child(Parent):
    @property
    def providerType(self) -> typing.Type[ParentProvider]:
        # Using Type[ChildProvider] instead Type[ParentProvider]
        # doesn't helps
        return ChildProvider

    @property
    def providerKwargs(self) -> typing.Dict[str, typing.Any]:
        return dict(who='world')

    def useIt(self) -> None:
        # How to avoid the following error?
        # error: "ParentProvider" has no attribute "p"  [attr-defined]
        print(self.provider.p)


if __name__ == "__main__":
    Child().useIt()

1 Ответ

0 голосов
/ 21 июня 2020

У меня есть «generi c class», который реализует «generi c members»

Затем отметьте его как таковой:

from typing import Generic, TypeVar


_T = TypeVar('_T', bound='ParentProvider')


class Parent(Generic[_T], metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def providerType(self) -> typing.Type[_T]:
        pass

    @property
    @abc.abstractmethod
    def providerKwargs(self) -> typing.Dict[str, typing.Any]:
        pass

    @property
    def provider(self) -> _T:
        return self.providerType(**self.providerKwargs)

    @abc.abstractmethod
    def useIt(self) -> None:
        pass

Теперь спецификация c Child impl становится:

class Child(Parent[ChildProvider]):
    @property
    def providerType(self) -> typing.Type[ChildProvider]:
        return ChildProvider

    @property
    def providerKwargs(self) -> typing.Dict[str, typing.Any]:
        return dict(who='world')

    def useIt(self) -> None:
        print(self.provider.p)

Как избежать следующей ошибки?

error: Too many arguments for "ParentProvider"  [call-arg]

Подпись Parent.__init__ не разрешить передачу любых аргументов - вы можете ослабить это с помощью

class ParentProvider(metaclass=abc.ABCMeta):
    def __init__(self, *args, **kwargs) -> None:
        pass

(или просто

class ParentProvider(metaclass=abc.ABCMeta):
    def __init__(self, **kwargs) -> None:
        pass

, если вы не хотите разрешать передачу позиционных аргументов в конструкторы провайдеров).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...