Search code examples
pythonpycharmsubclasspython-typing

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


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

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.

I am also open to other options, as I am sure there is a better way.

Can you please help me figure out the correct way to handle this?


What I Have Tried

To emulate option 2., I tried using typing.Type as suggested here: Subclass in type hinting

However, this was not working for me.


Solution

  • You can give a type hint on my_class attribute in the beginning of class definition:

    class SubclassOfFoo(Foo):
        my_class: SubclassOfSomething  # <- here
    
        def get_something(self) -> SubclassOfSomething:
            return SubclassOfSomething()
    
        def do_something_special(self):
            self.my_class.something_special()
    

    After that there is no warning Unresolved attribute reference 'something_special' for class 'Something' from PyCharm inspection because now my_class is known to be SubclassOfSomething not Something.