类型提示 super().__init__ 使用具体类型调用

问题描述 投票:0回答:2

下面是两个类树。 每个都有一个基类,吃掉其邻居类的基类。

一旦派生到具体类,我也使用邻居类型作为依赖项。 该代码按预期工作,但是类型检查器抱怨道。 (我使用的是pycharm)

我认为从具体类型向下转型为基类型是可以的,因为基类始终只适用于向下转型的类型,而不依赖于具体类型的具体方面。

from typing import reveal_type


class BaseView:
    ...


class BaseController:
    def __init__(self, view: BaseView):
        self.view = view


class ConcreteView(BaseView):
    attr = "42"


class ConcreteController(BaseController):
    def __init__(self, view: ConcreteView):
        super().__init__(view)

    def meth(self):
        # gives an
        # Unresolved attribute reference 'attr' for class 'BaseView'
        # warning
        print(self.view.attr)
        # okay
        print(reveal_type(self.view))


if __name__ == '__main__':
    concrete_view = ConcreteView()
    controller = ConcreteController(concrete_view)
    controller.meth()

一半的解决方案是使用让打字检查员信服的工厂 然而,linter 仍然无法解决。 另外,工厂模式不是我喜欢的,它使事情变得复杂,只要满足类型检查器

from typing import reveal_type, Callable


class BaseView:
    ...


class BaseController:
    def __init__(self, view_factory: Callable):
        self.view = view_factory()


class ConcreteView(BaseView):
    attr = "42"

    def __init__(self):
        self.b = 10


class ConcreteController(BaseController):
    def __init__(self, view_factory: Callable[[], ConcreteView]):
        super().__init__(view_factory)

    def meth(self):
        # warning gone but lint still not working
        print(self.view.attr)

        print(self.view.b)
        # okay
        print(reveal_type(self.view))


if __name__ == '__main__':
    controller = ConcreteController(lambda: ConcreteView())
    controller.meth()

另外我已经玩过通用了。

from typing import reveal_type, Generic, TypeVar

T = TypeVar("")


class BaseView(Generic[T]):
    ...


class BaseController(Generic[T]):
    def __init__(self, view: T):
        self.view = view


class ConcreteView(BaseView):
    attr = "42"


class ConcreteController(BaseController):
    def __init__(self, view: BaseView[ConcreteView]):
        super().__init__(view)

    def meth(self):
        # no warning typchecker
        print(self.view.attr)
        # but no lint
        self.view
        # okay
        print(reveal_type(self.view))


if __name__ == '__main__':
    concrete_view = ConcreteView()
    controller = ConcreteController(concrete_view)
    controller.meth()
python pycharm type-hinting super typechecking
2个回答
0
投票

因此,泛型的简单应用使得此过程既可以通过 mypy 也可以通过pyright,并且显示的类型是

ConcreteVeiw
:

import typing


class BaseView:
    pass


T = typing.TypeVar("T", bound=BaseView)


class BaseController(typing.Generic[T]):
    def __init__(self, view: T):
        self.view = view


class ConcreteView(BaseView):
    attr = "42"


class ConcreteController(BaseController[ConcreteView]):
    def __init__(self, view: ConcreteView):
        super().__init__(view)

    def meth(self) -> None:
        print(self.view.attr)
        typing.reveal_type(self.view)

这可能是我的第一直觉,但请注意,以下没有泛型的版本只需在

view: ConcreteView
also
中注释 ConcreteController 即可:

import typing


class BaseView:
    pass


class BaseController:
    def __init__(self, view: BaseView):
        self.view = view


class ConcreteView(BaseView):
    attr = "42"


class ConcreteController(BaseController):
    view: ConcreteView
    def __init__(self, view: ConcreteView):
        super().__init__(view)

    def meth(self) -> None:
        print(self.view.attr)
        typing.reveal_type(self.view)


if __name__ == "__main__":
    concrete_view = ConcreteView()
    controller = ConcreteController(concrete_view)
    controller.meth()

我当前的笔记本电脑上没有 PyCharm。但我觉得,如果在上述任何一种情况下都无法推断出

ConcreteView
,那么可能是时候更换编辑器了。或者简单地依靠
mypy
pyright
进行静态分析。公平地说,如果没有明确的
view: ConcreteView
,pyright 和 mypy 就会失败并抱怨您正在使用
BaseView
。但在这种情况下,明确地帮助类型推断总是好的。


0
投票

您的最后一次尝试已接近尾声。但是,您并没有真正使用泛型在具体类中键入视图。

解决方案

from typing import Generic, TypeVar

T = TypeVar("T")


class BaseView:
    ...


class BaseController(Generic[T]):
    def __init__(self, view: T) -> None:
        self.view = view


class ConcreteView(BaseView):
    attr = "42"


class ConcreteController(BaseController[ConcreteView]):
    def __init__(self, view: ConcreteView) -> None:
        super().__init__(view)

    def meth(self) -> None:
        # no warning typchecker
        print(self.view.attr)
        # but no lint
        self.view
        # okay
        print(type(self.view))


if __name__ == "__main__":
    concrete_view = ConcreteView()
    controller = ConcreteController(concrete_view)
    controller.meth()

注释

  • 完整类型提示包括所有方法的返回类型
  • Generic 是一个模板,您可以提供具体的类型
  • 在具体控制器类中,您使用具体视图来实现基本控制器。因此,您可以使用
    BaseController
    模板中的类型。
  • 在继承(子类)中,如果您键入输入参数作为类的祖先(基类),您仍然可以传递该类。
  • 在您的示例中,需要解决的问题是 linting。检查 PyCharm(内置)和 Pyre。两者都正确提供 linting(具有
    attr
    属性)。
© www.soinside.com 2019 - 2024. All rights reserved.