Search code examples
pythoninheritancepycharmtype-hintingbase-class

type annotation with inheritance in python


I am trying to provide type hints for a class init method, that uses child classes of a specific base class. Gooling how to do this tells me to use Type[BaseClass] as annotation, however inspections keep telling me i'm wrong.

As an example for my problem, let's say this is base.py:

class B:
    def __init__(self):
        return

..and another class.py...

class SomeClass:

    def __init__(self, some_param: Type[B]):
        self.sp = some_param

Now, using these classes in the following way in a third file test.py brings forth some confusion for me:

from base import B
from class import SomeClass 

if __name__ == '__main__':
    sc = SomeClass(B())

I would have guessed this to be correct, yet the pycharm inspections underline it with the hint:

Excpected type 'Type[B]', got 'B' instead

So since this didn't work, I figured I might need to use a TypeVar, so I changed base.py to:

class B:
    def __init__(self):
        return

TB = TypeVar('TB', bound=B)

and class.py to:

class SomeClass:

    def __init__(self, some_param: Type[TB]):
        self.sp = some_param

However, this merely changes the pycharm inspection to say:

Excpected type 'Type[TB]', got 'B' instead

Finally, if I set class.py to:

class SomeClass:

    TB = TypeVar('TB', bound=B)

    def __init__(self, some_param: Type[TB]):
        self.sp = some_param

The inspection error in test.py is gone and there are no complaints from pycharm!

(Curiously though, there are still complaints if I move if __name__ == '__main__': sc = SomeClass(B()) to class.py.)

As I said I'm a bit puzzled as to how to truly use base classes with typing in Python:

  • Is this all just a pycharm error?

If not,

  • why is Type[B] not enough/working?
  • why would one need to specify a TypeVar within the class and can't simply import the TypeVar?

Solution

  • Type[B] is for indication that the argument should be a class object itself.

    For example,

    class A:
        pass
    
    class B:
        pass
    
    class C(B):
        pass
    
    def foo(x: Type[B]):
        pass
    
    foo(A)    # fails
    foo(B)    # passes
    foo(C)    # passes
    foo(B())  # fails - an instance of B is not B or a subclass of B
    

    You just want B as the type hint, to allow an instance of B (or a subclass of B) as an argument.

    class SomeClass:
    
        def __init__(self, some_param: B):
            self.sp = some_param