Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PEP 646] Explicitly allow *args: *Tuple[*Ts, T] #2125

Closed
wants to merge 7 commits into from
87 changes: 74 additions & 13 deletions pep-0646.rst
Original file line number Diff line number Diff line change
Expand Up @@ -414,16 +414,25 @@ Normal ``TypeVar`` instances can also be prefixed and/or suffixed:
z = prefix_tuple(x=0, y=(True, 'a'))
# Inferred type of z is Tuple[int, bool, str]

``*args`` as a Type Variable Tuple
----------------------------------
Heterogeneous ``*args`` Types
-----------------------------

PEP 484 states that when a type annotation is provided for ``*args``, every argument
must be of the type annotated. That is, if we specify ``*args`` to be type ``int``,
then *all* arguments must be of type ``int``. This limits our ability to specify
the type signatures of functions that take heterogeneous argument types.

If ``*args`` is annotated as a type variable tuple, however, the types of the
individual arguments become the types in the type variable tuple:
With the new use for the star operator introduced in this PEP, however, we
can do better. In short, if ``*args`` is annotated as a star-prefixed type
tuple or type variable tuple, the types of the individual arguments are bound
to the types within the star-prefixed container.

Using a Type Variable Tuple
'''''''''''''''''''''''''''

For example, annotating ``*args`` as a star-prefixed type variable tuple
results in the types in the type variable tuple being bound to the types
of the individual arguments:

::

Expand All @@ -446,8 +455,47 @@ instance is *not* allowed:

``*args`` is the only case where an argument can be annotated as ``*Ts`` directly;
other arguments should use ``*Ts`` to parameterise something else, e.g. ``Tuple[*Ts]``.
If ``*args`` itself is annotated as ``Tuple[*Ts]``, the old behaviour still applies:
all arguments must be a ``Tuple`` parameterised with the same types.

Also note that, following `Type Variable Tuples Must Have Known Length`_,
the following should *not* type-check as valid (even though it is, of
course, valid at runtime):

::

def foo(*args: *Ts): ...

def bar(x: Tuple[int, ...]):
foo(*x) # NOT valid

Using a Tuple of Types
''''''''''''''''''''''

Similarly, ``*args`` can be annotated as a star-prefixed tuple type:

::

def foo(*args: *Tuple[int, int]): ...

This is very similar to:

::

def bar(arg1: int, arg2: int): ...

However, ``bar`` is subtly different than ``foo``: a) ``foo`` may be more
convenient if e.g. passing ``args`` on to something else, and b) ``foo``
disallows keyword arguments.

Note that arbitrary-length tuple types can also be used here:

::

def foo(*args: *Tuple[int, ...]): ...

This is roughly the same as ``*args: int``.

Also note that if ``*args`` is annotated as an *unstarred* ``Tuple[*Ts]``, the
old ``*args`` behaviour still applies: all arguments must be a ``Tuple`` parameterised with the same types.

::

Expand All @@ -457,18 +505,31 @@ all arguments must be a ``Tuple`` parameterised with the same types.
foo((0,), (1, 2)) # Error
foo((0,), ('1',)) # Error

Following `Type Variable Tuples Must Have Known Length`_, note
that the following should *not* type-check as valid (even though it is, of
course, valid at runtime):
Using a Type Variable Tuple Inside a Tuple
''''''''''''''''''''''''''''''''''''''''''

Finally, by using a type variable tuple inside a tuple of other
types, we can refer to prefixes or suffixes of the variadic
argument list. For example:

::

def foo(*args: *Ts): ...
# os.execle takes arguments 'path, arg0, arg1, ..., env'
def execle(path: str, *args: *Tuple[*Ts, Mapping[str, str]]) → None: ...

def bar(x: Tuple[int, ...]):
foo(*x) # NOT valid
Note this this is different to

::

def execle(path: str, *args: *Ts, env: Mapping[str, str]) → None: ...

as this would make ``env`` a keyword-only argument.

What About ``**kwargs?``
''''''''''''''''''''''''

Finally, note that a type variable tuple may *not* be used as the type of
The PEP does *not* introduce any new behaviour for typing ``**kwargs``.
In particular, a type variable tuple may *not* be used as the type of
``**kwargs``. (We do not yet know of a use case for this feature, so we prefer
to leave the ground fresh for a potential future PEP.)

Expand Down