Define a variadic return value

Hi.

I’m struggling trying to define a function that takes multiple parameters and build a tuple with “dependant” types. Here a (very simplified) scenario

class C1:
    def __init__(self, foo: str) -> None: ...

class C2:
    def __init__(self, foo: str) -> None: ...

type Cls[T] = Callable[[str], T]

def bar[T, *Ts](foo: str, *clss: Cls[T]) -> tuple[T, *Ts]:
    return tuple(cls(foo) for cls in clss)

c1, c2 = foo('...', C1, C2)

Keep in mind that C1 and C2 are not nominally related, there is no “base” class (in fact in my case they also came from a 3rd party library) but they share the same __init__ signature.

For this reason I’m relying on a generic type Cls[T] (but this is my stuff, so I can change it if it’s not useful)

My goal is to have that c1, c2 = bar('...', C1, C2) with bar annotated such as that c1 is recognized as instance of C1, and c2 of C2.

The tricky part - for me - is that I’m struggling to describe the type of clss. I have tried looking at ParamSpec and at TypeVarTuple but I cannot find a simple way to say something like "I want the first element ofclss of type Cls[T], the second one as Cls[Ts[0]] , and so on…

Even with ParamSpec I may be able to access P.args, but I want to “map” them to Cls , but I cannot find a way to use them.

Can you help me?


at worst, I can create a bunch of @overload’ed signatures and change a bit the implementation, but it is a very limited solution…

@overload
def bar[T1](foo: str, /, cls1: Cls[T1]) -> tuple[T1]: ...
@overload
def bar[T1, T2](foo: str, /, cls1: Cls[T1], cls2: Cls[T2]) -> tuple[T1, T2]: ...
@overload
def bar[T1, T2, T3](foo: str, /, cls1: Cls[T1], cls2: Cls[T2], cls3: Cls[T3]) -> tuple[T1, T2, T3]: ...

def bar[T1, T2, T3](foo: str, /, cls1: Cls[T1], cls2: Cls[T2] | None = None, cls3: Cls[T3] | None = None) -> tuple[T1] | tuple[T1, T2] | tuple[T1, T2, T3]:
    # ... make it work manually handling cls2 = None and cls3 = None

It is unfortunate that the current type system cannot satisfy your need, which I must add is not uncommon. See, for example, the unholy amount of zip constructor overloads in typeshed starting here, or the more_itertools stubs.