I can't think of any use cases either. It's super unusual in Python to have a variable number arguments followed by a fixed argument, since the function being called would have to slice args to get at it. But I thought the post on python-dev that you linked to was about something else? It uses the builtin function enumerate(), which takes an iterable of values like ['x', 'y', 'z'] and turns it into a stream of pairs (0, 'x'), (1, 'y'), (2, 'z'). It then defines a function enumerate_args(f, args) which calls f(*enumerate(args)), i.e. if args has length N, then f takes N arguments, each of which has type Tuple[int, T] where T is the item type of args. In my example ['x', 'y', 'z'], N is 3 and T is str, so enumerate_args(f, ['x', 'y', 'z']) will end up calling f((0, 'x'), (1, 'y'), (2, 'z'). I guess the type of f is then (*args: Tuple[int, T]) ->None but this doesn't capture the length of args, N. The response on python-dev indicates that the solution requires Map[]. But maybe I've missed the connection between that post and your idea of allowing *args, *Tuple[*Ts, int] in a parameter list? On Thu, Oct 14, 2021 at 5:05 AM Matthew Rahtz via Typing-sig < typing-sig@python.org> wrote:
In the last tensor typing meeting we realised there's something missing from PEP 646: the ability to split off suffixes from *args. (Thanks for Pradeep for pointing this out, based on discussions with Eric Traut and this question on python-dev https://mail.python.org/archives/list/python-dev@python.org/thread/K76H6C5JW... )
*Intro*
To recap, PEP 646 allows us to bind the types of *args into a TypeVarTuple:
Ts = TypeVarTuple('Ts') def foo(*args: *Ts): ... foo(1, '2') # Ts is bound to Tuple[int, str]
But what if we wanted to do something like the following?
def foo( *args: [types of all positional args but the last bound to Ts; type of last positional arg should be int] ) -> Tuple[*Ts]: ...
x = foo(True, 1.0, 2) # x should have type Tuple[bool, float]
*The problem*
You might think the answer is:
def foo( *args: *Ts, final_arg: int, ) -> Tuple[*Ts]: ...
But note that this means something different; all arguments after *args must be specified as keywords:
foo(True, 1.0, 2) TypeError: foo() missing 1 required keyword-only argument: 'final_arg'
Note that this is an only an issue for suffixes of variadic argument lists; prefixes are fine:
def foo( first_arg: int, *args: *Ts, ) -> Tuple[*Ts]: ...
*Proposed solution*
We think the way of encoding what we want most consistent with the rest of PEP 646 would be something like:
def foo( *args: *Tuple[*Ts, int], ) -> Tuple[*Ts]: ...
Why? Well, when we say `*args: *Ts`, we're saying "Put all the types of `args` into the thing that comes after the starred part of the annotation". So here, we're saying "Put all the types of `args` into Tuple[*Ts, int]" - that is, doing some pattern matching, "Put all types but the last into `Ts`".
*Use cases?*
This is (for me) an unintuitive enough construction that I think our first question should be, does this have plausible enough use cases to support the complexity?
The only one I can think off of the top of my head is something like:
def log_with_logger(*args: *Tuple[*Ts, Logger]): ... log_with_logger('foo', 'bar', logger_inst)
But here the issue could be worked around by just moving the position of the logger argument:
def log_with_logger(logger: Logger, *args: *Ts): ...
So I'm not sure I really believe this example.
Can anyone else think of any use cases for this?
---
Thanks! Matthew _______________________________________________ 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: guido@python.org
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>