PEP 646 – Variadic Generics multiple matching variants
![](https://secure.gravatar.com/avatar/a81ff4bd152435a8f72026da46937b5c.jpg?s=120&d=mm&r=g)
Have this example: ``` from __future__ import annotations from typing import Generic from typing import TypeVar from typing import TypeVarTuple Shape = TypeVarTuple("Shape") T = TypeVar("T") T1 = TypeVar("T1") class Array(Generic[*Shape]): ... y: Array[int, *tuple[float, ...], int, str] = Array() def expect_variadic_array(x: Array[int, T, *Shape, T1]) -> Array[*Shape, T, T1]: ... expr = expect_variadic_array(y) which type is expected for expr? ``` Here we can assign: ``` int -> int T -> int (*float empty) *Shape -> empty T1 -> str so return Array[int, str] ``` or ``` int -> int T -> float *Shape -> int T1 -> str ``` so T can be either int or float and *Shape can be either () or (int, float*)
![](https://secure.gravatar.com/avatar/3247bfd67c8fa4c4a08b8d30e49eab39.jpg?s=120&d=mm&r=g)
I think it's indeterminate and undefined. I recommend against relying on something like this. Different type checkers will inevitably produce different answers here. Similar ambiguities arise for unpacking and assigning arguments to parameters when indeterminate-length tuples are involved. ``` def func(y: tuple[int, *tuple[float, ...], int, str]): a, b, *c, d = y ``` -Eric -- Eric Traut Contributor to Pyright & Pylance Microsoft
![](https://secure.gravatar.com/avatar/7b2fa384c7f1fcaef1e99d297b9a624b.jpg?s=120&d=mm&r=g)
Apologies for the slow reply - I'm only just now finally catching up after vacation. If we're substituting: int, *tuple[float, ...], int, str Into: int, T, *Shape, T1 Then I believe what should actually happen is: T = float T1 = str Shape = tuple[*tuple[float, ...], int] The reasoning is that, for arbitrary-length tuples like tuple[float, ...], we assume it always has enough items to fill all the type variables. We chose this behaviour in order to be consistent with existing behaviour for arbitrary-length tuples: t: tuple[int, ...] a, b = t # Valid a, b, c = t # Also valid def bar(a: int, *b: int): ... bar(*t) # Valid Note that this last example is somewhat surprising - `bar` supposedly has to take at least one argument, but the arbitrary-length tuple we're passing in could have zero elements. But *shrug* this is the way it is. This is admittedly rather under-documented. We're trying to amend PEP 646 in https://github.com/python/peps/pull/2883 with the details of how this works. If the SC decides against revising an already-accepted PEP, though, we'll try and find somewhere else to document it. On Wed, 2 Nov 2022 at 17:03, Eric Traut <eric@traut.com> wrote:
I think it's indeterminate and undefined. I recommend against relying on something like this. Different type checkers will inevitably produce different answers here.
Similar ambiguities arise for unpacking and assigning arguments to parameters when indeterminate-length tuples are involved.
``` def func(y: tuple[int, *tuple[float, ...], int, str]): a, b, *c, d = y ```
-Eric
-- Eric Traut Contributor to Pyright & Pylance Microsoft _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: mrahtz@google.com
participants (3)
-
andrey.matveev.mano@gmail.com
-
Eric Traut
-
Matthew Rahtz