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

Int*_*rer 9 python typing subclass type-hinting pycharm

Before you dive in, here is my question: how can I use type hints in a subclass to specify a different type on an instance attribute?

If you are unclear on what that means, read below, where I have drawn up an example to clarify things.


Full Explanation

I have an abstract class Foo, and a subclass of Foo called SubclassOfFoo.

Foo has an abstract method get_something that returns an object of type Something.

Something has a subclass called SubclassOfSomething. SubclassOfSomething has an additional method something_special.

SubclassOfFoo overrides get_something to return an object of type SubclassOfSomething. Then, SubclassOfFoo tries to use SubclassOfSomething's method something_special.

However, currently my PyCharm's inspections are reporting Unresolved attribute reference 'something_special' for class 'Something'. I am trying to figure out the correct way to fix this.

This is all very confusing, so I have made a nice little code snippet to help here:


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

Run Code Online (Sandbox Code Playgroud)

Basically, in order to get everything to work out, I can do one of several things:

  1. Remove the type hint on the return of get_something within Foo
  2. Use a type hint in SubclassOfFoo for self.my_class to clear things up
  3. Use generics?

Option #1 is what I am trying to avoid

Option #2 is not bad, but I can't figure it out

Option #3 is also an option.

我也愿意接受其他选择,因为我相信有更好的方法。

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


我试过的

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

但是,这对我不起作用。

san*_*ash 8

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()
Run Code Online (Sandbox Code Playgroud)

之后,Unresolved attribute reference 'something_special' for class 'Something'PyCharm 检查就不会发出警告,因为现在my_class已知不是SubclassOfSomething这样Something

  • @IntrastellarExplorer 请检查此 PEP:https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations。据说,在类作用域中描述为“my_class: SubclassOfSomething”的变量被视为实例属性,而“my_class: ClassVar[SubclassOfSomething]”则被视为类变量,而不是实例属性。 (2认同)

use*_*698 6

使用泛型:

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()
Run Code Online (Sandbox Code Playgroud)