Advanced generic type annotations

145 views Asked by At

I have a Wrapper class (<<T.init>> is the part I am struggling with):

T = TypeVar('T')

class Wrapper(Generic[T]):
    def __init__(**kwargs: <<T.__init__>>):
        self.kwargs = kwargs
        ...

    def __call__() -> T:
        return self.__orig_class__.__args__[-1](**self.kwargs)

I am using it like this:

wrapped_class = Wrapper[SomeClass](**SomeClass_arguments)

SomeClass_arguments are arguments that the SomeClass takes to its init method

I want to make static type checking and hinting work for SomeClass_arguments. I was able to check them dynamically using the inspect module. But I would much rather prefer a static solution. I don't have always control over SomeClass as it can be either my class or some third-party class.

I cannot discuss the true intention of this design. But I need to wrap (add some levels of functionality) an object and at the same time separate the object definition wrapped_class = Wrapper[SomeClass](**SomeClass_arguments) and initialization wrapped_class().

1

There are 1 answers

8
David On

You can try this:

from typing import Callable, Generic, ParamSpec, TypeVar

T = TypeVar('T')
P = ParamSpec('P')

class Wrapper(Generic[T]):
    def __init__(self, cls: Callable[P, T], *args: P.args, **kwargs: P.kwargs):
        self._cls = cls
        self.args = args
        self.kwargs = kwargs

    def __call__(self) -> T:
        return self._cls(*self.args, **self.kwargs)

wrap = Wrapper(int, '123')  # OK
wrap = Wrapper(int, [])  # gives error: list not compatible with arguments of int

It uses a Callable[P, T] where P is a ParamSpec to specify the type. The P is inferred from the first argument and the arguments passed.