繁体   English   中英

Python:如何覆盖子类中实例属性的类型提示?

[英]Python: how to override type hint on an instance attribute in a subclass?

在深入研究之前,我的问题是:如何在子类中使用类型提示来指定实例属性的不同类型?

如果您不清楚这意味着什么,请阅读以下内容,我在其中制定了一个示例来澄清事情。


完整说明

我有一个抽象的 class Foo和一个名为SubclassOfFooFoo子类。

Foo有一个抽象方法get_something ,它返回 Some 类型的Something

Something有一个名为SubclassOfSomething的子类。 SubclassOfSomething有一个附加方法something_special

SubclassOfFoo覆盖get_something以返回SubclassOfSomething类型的 object 。 然后, SubclassOfFoo尝试使用SubclassOfSomething的方法something_special

但是,目前我的 PyCharm 的检查报告Unresolved attribute reference 'something_special' for class 'Something' 我正在尝试找出解决此问题的正确方法。

这一切都非常令人困惑,所以我制作了一个很好的小代码片段来帮助这里:


from abc import ABC, abstractmethod


class Something:
    def __init__(self):
        self.attr = 0


class SubclassOfSomething(Something):
    def __init__(self):
        Something.__init__(self)

    def something_special(self):
        self.attr = 1


class Foo(ABC):
    def __init__(self):
        self.my_class = self.get_something()

    @abstractmethod
    def get_something(self) -> Something:
        pass


class SubclassOfFoo(Foo):
    def __init__(self):
        Foo.__init__(self)

    def get_something(self) -> SubclassOfSomething:
        return SubclassOfSomething()

    def do_something_special(self):
        self.my_class.something_special()

基本上,为了让一切顺利进行,我可以做以下几件事之一:

  1. 删除Fooget_something返回的类型提示
  2. SubclassOfFoo中为self.my_class使用类型提示来清除问题
  3. 使用 generics?

选项 #1 是我试图避免的

选项#2不错,但我想不通

选项#3 也是一个选项。

我也对其他选择持开放态度,因为我确信有更好的方法。

你能帮我找出处理这个问题的正确方法吗?


我试过的

为了模拟选项#2,我尝试按照这里的建议使用typing.Type类型提示中的子类

但是,这对我不起作用。

您也可以在Something上提供something_special方法,并引发NotImplementedError

class Something:
    def __init__(self):
        self.attr = 0

    def something_special(self):
        raise NotImplementedError()

这解决了您的类型提示问题,尽管在功能上它会在同一点引发异常(如果您设法以某种方式获得了Something并尝试调用something_special ,则将是NotImplementedError而不是AttributeError )。

也许在某些情况下,您可能只想pass ,这取决于something_special实际上是什么。

class Something:
    def __init__(self):
        self.attr = 0

    def validate(self):
        # doesn't want to perform validation
        pass


class SubclassOfSomething(Something):
    def __init__(self):
        Something.__init__(self)

    def validate(self):
        if self.attr < 0:
            raise ValueError()

重要的基础是确保您的 class 层次结构符合公共接口 - 子类上的公共方法而不是父类上的公共方法违背了这一点,并减少了 class 层次结构中对象的多态性。

您可以在 class 定义的开头给出my_class属性的类型提示:

class SubclassOfFoo(Foo):
    my_class: SubclassOfSomething  # <- here

    def get_something(self) -> SubclassOfSomething:
        return SubclassOfSomething()

    def do_something_special(self):
        self.my_class.something_special()

之后没有警告Unresolved attribute reference 'something_special' for class 'Something' from PyCharm 检查,因为现在my_class已知为SubclassOfSomething not Something

使用 generics:

from abc import ABC, abstractmethod
from typing import Generic, TypeVar


SomethingT = TypeVar('SomethingT', bound='Something')


...


class Foo(ABC, Generic[SomethingT]):
    my_class: SomethingT

    def __init__(self):
        self.my_class = self.get_something()

    @abstractmethod
    def get_something(self) -> SomethingT:
        pass


class SubclassOfFoo(Foo[SubclassOfSomething]):
    def __init__(self):
        super().__init__()

    def get_something(self) -> SubclassOfSomething:
        return SubclassOfSomething()

    def do_something_special(self):
        # inferred type of `self.my_class` will be `SubclassOfSomething`
        self.my_class.something_special()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM