Search code examples
pythonpython-3.xinheritancetyping

How to indicate an argument should be a reference to derived class in python typing?


I would like to to pass class itself as an argument in the constructor. I know it is possible in Python but I am having problems understanding how should I write a proper typing annotation. Use case is the following:

In the constructor of class A I want to pass a reference to some class X (not an object of class X) that inherits after BaseX. Both X and BaseX come from a library. Apart from the reference to X, constructor of A accepts arguments that help build X:

# Library
class BaseX:
    def func():
        print("Hey BaseX")

class X(BaseX):
    def func():
        print("X!")

# My client code
class A:
    def __init__(arg x, arg y, layer: BaseX): # what should be the annotation of layer?
        # construct BaseX object
        self.layer = BaseX(x=x, y=y) # IDEs show x and y as  unexpected arguments because they treat BaseX as an object and look into its __call__ func

A(5, 6, X)

I am unsure how can I express the annotation of layer so that it can be treated as a class and ensure its a derivative of BaseX. I would also like to ask about some comment about whether this is a Pythonic way to do this.

Cheers!


Solution

  • You can indicate that a variable is a reference to a type with the annotation Type[BaseX] (see Python docs on Type). A variable annotated with Type[T] holds any type that is a subtype of T.

    For your specific use case of "constructing an object of the specified type, which is a subtype of BaseX", you can use more accurate annotations with the help of TypeVar. For example:

    T = TypeVar('T', bound=BaseX)
    
    def construct(cls: Type[T], *args, **kwargs) -> T:
        return cls(*args, **kwargs)
    

    Here:

    • TypeVar('T', bound=BaseX) defines a "type variable" that can be substituted with any type "bounded" by BaseX, i.e., is a subtype of BaseX.
    • The construct function takes an argument cls with annotation Type[T], indicating it's a reference to a subtype of BaseX.
    • The return type annotation is T, indicating the returned value is an instance of the subtype of BaseX.
    • All occurrences of a type variable within a function or class are bound to the same type. In this case, the type of the returned value is the type passed as argument.