【问题标题】:Python type annotation issues with inheriting types and members overload继承类型和成员重载的 Python 类型注释问题
【发布时间】:2020-06-18 09:37:44
【问题描述】:

请参阅下面的示例,使用 python 3.7,我无法找到正确注释的方法。注释错误以 cmets 显示,由 mypy 给出。

  • 我有一个实现“通用成员”的“通用类”。以及继承该结构的具体类和成员。
  • 具体成员可以有其他方法并为构造函数使用不同的参数。

正确注释的方法是什么?

非常感谢。

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()

【问题讨论】:

  • 对于“Child.useIt”方法,解决方法是在使用 self.provider.p 之前“断言 isinstance(self.provider, self.providerType)”。但这在 Parent.provider 中不起作用,因为它还不知道最终的 providerType 类型。
  • 这里有几个不清楚的地方。您将关键字参数传递给providerType,但您没有将其定义为接受任何参数。 providerKwargs 是一种方法,但您没有调用它(您将其视为 dict 本身)。
  • 谢谢@chepner。在 Parent.provider 属性中实例化提供程序时,Parent 使用 providerKwargs。
  • 谁用没关系,因为你用错了。如果它是一个方法,你必须调用它:return self.providerType(**self.providerKwargs())。并且您仍然需要将providerType 的签名更改为accept 关键字参数。
  • @chepner providerKwargs 是一个属性...

标签: python python-3.x mypy type-annotation


【解决方案1】:

我有一个实现“通用成员”的“通用类”

然后这样标记:

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

现在具体的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

如果您不想允许在提供者构造函数中传递位置参数)。

【讨论】:

  • 乐于助人! :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-20
相关资源
最近更新 更多