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

You could provide the something_special method on Something too, and raise a NotImplementedError

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

    def something_special(self):
        raise NotImplementedError()

This resolves your type hinting issue, although functionally it will raise an exception at the same point (if you managed to get a Something somehow and try to call something_special, just will be NotImplementedError instead of AttributeError).

Maybe in some situations you might want to just pass instead, depending on what something_special actually is.

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

The important underlying thing is making sure your class hierarchy conforms to a common interface - public methods on subclasses but not on parents goes against that and reduces the polymorphism of objects in your class hierarchy.


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

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.