[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