[Python-checkins] bpo-47097: Add documentation for TypeVarTuple (#32103)
JelleZijlstra
webhook-mailer at python.org
Mon Apr 4 19:37:11 EDT 2022
https://github.com/python/cpython/commit/38ae5b8c0c0b64ae6100b0dee8707d5ab769e381
commit: 38ae5b8c0c0b64ae6100b0dee8707d5ab769e381
branch: main
author: Matthew Rahtz <matthew.rahtz at gmail.com>
committer: JelleZijlstra <jelle.zijlstra at gmail.com>
date: 2022-04-04T16:37:01-07:00
summary:
bpo-47097: Add documentation for TypeVarTuple (#32103)
Co-authored-by: Alex Waygood <Alex.Waygood at Gmail.com>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra at gmail.com>
files:
M Doc/library/typing.rst
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 4d833dc497f17..37c17c429fa47 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -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
More information about the Python-checkins
mailing list