mirror of
https://github.com/python/cpython
synced 2026-09-29 12:10:30 +03:00
bpo-47097: Add documentation for TypeVarTuple (#32103)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
c9844cb8aa
commit
38ae5b8c0c
1 changed files with 119 additions and 0 deletions
|
|
@ -70,6 +70,8 @@ annotations. These include:
|
|||
*Introducing* :class:`ParamSpec` and :data:`Concatenate`
|
||||
* :pep:`613`: Explicit Type Aliases
|
||||
*Introducing* :data:`TypeAlias`
|
||||
* :pep:`646`: Variadic Generics
|
||||
*Introducing* :data:`TypeVarTuple`
|
||||
* :pep:`647`: User-Defined Type Guards
|
||||
*Introducing* :data:`TypeGuard`
|
||||
* :pep:`673`: Self type
|
||||
|
|
@ -1230,6 +1232,123 @@ These are not used in annotations. They are building blocks for creating generic
|
|||
``covariant=True`` or ``contravariant=True``. See :pep:`484` for more
|
||||
details. By default, type variables are invariant.
|
||||
|
||||
.. class:: TypeVarTuple
|
||||
|
||||
Type variable tuple. A specialized form of :class:`Type variable <TypeVar>`
|
||||
that enables *variadic* generics.
|
||||
|
||||
A normal type variable enables parameterization with a single type. A type
|
||||
variable tuple, in contrast, allows parameterization with an
|
||||
*arbitrary* number of types by acting like an *arbitrary* number of type
|
||||
variables wrapped in a tuple. For example::
|
||||
|
||||
T = TypeVar('T')
|
||||
Ts = TypeVarTuple('Ts')
|
||||
|
||||
def remove_first_element(tup: tuple[T, *Ts]) -> tuple[*Ts]:
|
||||
return tup[1:]
|
||||
|
||||
# T is bound to int, Ts is bound to ()
|
||||
# Return value is (), which has type tuple[()]
|
||||
remove_first_element(tup=(1,))
|
||||
|
||||
# T is bound to int, Ts is bound to (str,)
|
||||
# Return value is ('spam',), which has type tuple[str]
|
||||
remove_first_element(tup=(1, 'spam'))
|
||||
|
||||
# T is bound to int, Ts is bound to (str, float)
|
||||
# Return value is ('spam', 3.0), which has type tuple[str, float]
|
||||
remove_first_element(tup=(1, 'spam', 3.0))
|
||||
|
||||
Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``.
|
||||
Conceptually, you can think of ``Ts`` as a tuple of type variables
|
||||
``(T1, T2, ...)``. ``tuple[T, *Ts]`` would then become
|
||||
``tuple[T, *(T1, T2, ...)]``, which is equivalent to
|
||||
``tuple[T, T1, T2, ...]``. (Note that in older versions of Python, you might
|
||||
see this written using :data:`Unpack <Unpack>` instead, as
|
||||
``Unpack[Ts]``.)
|
||||
|
||||
Type variable tuples must *always* be unpacked. This helps distinguish type
|
||||
variable types from normal type variables::
|
||||
|
||||
x: Ts # Not valid
|
||||
x: tuple[Ts] # Not valid
|
||||
x: tuple[*Ts] # The correct way to to do it
|
||||
|
||||
Type variable tuples can be used in the same contexts as normal type
|
||||
variables. For example, in class definitions, arguments, and return types::
|
||||
|
||||
Shape = TypeVarTuple('Shape')
|
||||
class Array(Generic[*Shape]):
|
||||
def __getitem__(self, key: tuple[*Shape]) -> float: ...
|
||||
def __abs__(self) -> Array[*Shape]: ...
|
||||
def get_shape(self) -> tuple[*Shape]: ...
|
||||
|
||||
Type variable tuples can be happily combined with normal type variables::
|
||||
|
||||
DType = TypeVar('DType')
|
||||
|
||||
class Array(Generic[DType, *Shape]): # This is fine
|
||||
pass
|
||||
|
||||
class Array2(Generic[*Shape, DType]): # This would also be fine
|
||||
pass
|
||||
|
||||
float_array_1d: Array[float, Height] = Array() # Totally fine
|
||||
int_array_2d: Array[int, Height, Width] = Array() # Yup, fine too
|
||||
|
||||
However, note that at most one type variable tuple may appear in a single
|
||||
list of type arguments or type parameters::
|
||||
|
||||
x: tuple[*Ts, *Ts] # Not valid
|
||||
class Array(Generic[*Shape, *Shape]): # Not valid
|
||||
pass
|
||||
|
||||
Finally, an unpacked type variable tuple can be used as the type annotation
|
||||
of ``*args``::
|
||||
|
||||
def call_soon(
|
||||
callback: Callable[[*Ts], None],
|
||||
*args: *Ts
|
||||
) -> None:
|
||||
...
|
||||
callback(*args)
|
||||
|
||||
In contrast to non-unpacked annotations of ``*args`` - e.g. ``*args: int``,
|
||||
which would specify that *all* arguments are ``int`` - ``*args: *Ts``
|
||||
enables reference to the types of the *individual* arguments in ``*args``.
|
||||
Here, this allows us to ensure the types of the ``*args`` passed
|
||||
to ``call_soon`` match the types of the (positional) arguments of
|
||||
``callback``.
|
||||
|
||||
For more details on type variable tuples, see :pep:`646`.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. data:: Unpack
|
||||
|
||||
A typing operator that conceptually marks an object as having been
|
||||
unpacked. For example, using the unpack operator ``*`` on a
|
||||
:class:`type variable tuple <TypeVarTuple>` is equivalent to using ``Unpack``
|
||||
to mark the type variable tuple as having been unpacked::
|
||||
|
||||
Ts = TypeVarTuple('Ts')
|
||||
tup: tuple[*Ts]
|
||||
# Effectively does:
|
||||
tup: tuple[Unpack[Ts]]
|
||||
|
||||
In fact, ``Unpack`` can be used interchangeably with ``*`` in the context
|
||||
of types. You might see ``Unpack`` being used explicitly in older versions
|
||||
of Python, where ``*`` couldn't be used in certain places::
|
||||
|
||||
# In older versions of Python, TypeVarTuple and Unpack
|
||||
# are located in the `typing_extensions` backports package.
|
||||
from typing_extensions import TypeVarTuple, Unpack
|
||||
|
||||
Ts = TypeVarTuple('Ts')
|
||||
tup: tuple[*Ts] # Syntax error on Python <= 3.10!
|
||||
tup: tuple[Unpack[Ts]] # Semantically equivalent, and backwards-compatible
|
||||
|
||||
.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)
|
||||
|
||||
Parameter specification variable. A specialized version of
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue