Skip to content

Commit 38ae5b8

Browse files
mrahtzAlexWaygoodJelleZijlstra
authored
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>
1 parent c9844cb commit 38ae5b8

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

Doc/library/typing.rst

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ annotations. These include:
7070
*Introducing* :class:`ParamSpec` and :data:`Concatenate`
7171
* :pep:`613`: Explicit Type Aliases
7272
*Introducing* :data:`TypeAlias`
73+
* :pep:`646`: Variadic Generics
74+
*Introducing* :data:`TypeVarTuple`
7375
* :pep:`647`: User-Defined Type Guards
7476
*Introducing* :data:`TypeGuard`
7577
* :pep:`673`: Self type
@@ -1230,6 +1232,123 @@ These are not used in annotations. They are building blocks for creating generic
12301232
``covariant=True`` or ``contravariant=True``. See :pep:`484` for more
12311233
details. By default, type variables are invariant.
12321234

1235+
.. class:: TypeVarTuple
1236+
1237+
Type variable tuple. A specialized form of :class:`Type variable <TypeVar>`
1238+
that enables *variadic* generics.
1239+
1240+
A normal type variable enables parameterization with a single type. A type
1241+
variable tuple, in contrast, allows parameterization with an
1242+
*arbitrary* number of types by acting like an *arbitrary* number of type
1243+
variables wrapped in a tuple. For example::
1244+
1245+
T = TypeVar('T')
1246+
Ts = TypeVarTuple('Ts')
1247+
1248+
def remove_first_element(tup: tuple[T, *Ts]) -> tuple[*Ts]:
1249+
return tup[1:]
1250+
1251+
# T is bound to int, Ts is bound to ()
1252+
# Return value is (), which has type tuple[()]
1253+
remove_first_element(tup=(1,))
1254+
1255+
# T is bound to int, Ts is bound to (str,)
1256+
# Return value is ('spam',), which has type tuple[str]
1257+
remove_first_element(tup=(1, 'spam'))
1258+
1259+
# T is bound to int, Ts is bound to (str, float)
1260+
# Return value is ('spam', 3.0), which has type tuple[str, float]
1261+
remove_first_element(tup=(1, 'spam', 3.0))
1262+
1263+
Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``.
1264+
Conceptually, you can think of ``Ts`` as a tuple of type variables
1265+
``(T1, T2, ...)``. ``tuple[T, *Ts]`` would then become
1266+
``tuple[T, *(T1, T2, ...)]``, which is equivalent to
1267+
``tuple[T, T1, T2, ...]``. (Note that in older versions of Python, you might
1268+
see this written using :data:`Unpack <Unpack>` instead, as
1269+
``Unpack[Ts]``.)
1270+
1271+
Type variable tuples must *always* be unpacked. This helps distinguish type
1272+
variable types from normal type variables::
1273+
1274+
x: Ts # Not valid
1275+
x: tuple[Ts] # Not valid
1276+
x: tuple[*Ts] # The correct way to to do it
1277+
1278+
Type variable tuples can be used in the same contexts as normal type
1279+
variables. For example, in class definitions, arguments, and return types::
1280+
1281+
Shape = TypeVarTuple('Shape')
1282+
class Array(Generic[*Shape]):
1283+
def __getitem__(self, key: tuple[*Shape]) -> float: ...
1284+
def __abs__(self) -> Array[*Shape]: ...
1285+
def get_shape(self) -> tuple[*Shape]: ...
1286+
1287+
Type variable tuples can be happily combined with normal type variables::
1288+
1289+
DType = TypeVar('DType')
1290+
1291+
class Array(Generic[DType, *Shape]): # This is fine
1292+
pass
1293+
1294+
class Array2(Generic[*Shape, DType]): # This would also be fine
1295+
pass
1296+
1297+
float_array_1d: Array[float, Height] = Array() # Totally fine
1298+
int_array_2d: Array[int, Height, Width] = Array() # Yup, fine too
1299+
1300+
However, note that at most one type variable tuple may appear in a single
1301+
list of type arguments or type parameters::
1302+
1303+
x: tuple[*Ts, *Ts] # Not valid
1304+
class Array(Generic[*Shape, *Shape]): # Not valid
1305+
pass
1306+
1307+
Finally, an unpacked type variable tuple can be used as the type annotation
1308+
of ``*args``::
1309+
1310+
def call_soon(
1311+
callback: Callable[[*Ts], None],
1312+
*args: *Ts
1313+
) -> None:
1314+
...
1315+
callback(*args)
1316+
1317+
In contrast to non-unpacked annotations of ``*args`` - e.g. ``*args: int``,
1318+
which would specify that *all* arguments are ``int`` - ``*args: *Ts``
1319+
enables reference to the types of the *individual* arguments in ``*args``.
1320+
Here, this allows us to ensure the types of the ``*args`` passed
1321+
to ``call_soon`` match the types of the (positional) arguments of
1322+
``callback``.
1323+
1324+
For more details on type variable tuples, see :pep:`646`.
1325+
1326+
.. versionadded:: 3.11
1327+
1328+
.. data:: Unpack
1329+
1330+
A typing operator that conceptually marks an object as having been
1331+
unpacked. For example, using the unpack operator ``*`` on a
1332+
:class:`type variable tuple <TypeVarTuple>` is equivalent to using ``Unpack``
1333+
to mark the type variable tuple as having been unpacked::
1334+
1335+
Ts = TypeVarTuple('Ts')
1336+
tup: tuple[*Ts]
1337+
# Effectively does:
1338+
tup: tuple[Unpack[Ts]]
1339+
1340+
In fact, ``Unpack`` can be used interchangeably with ``*`` in the context
1341+
of types. You might see ``Unpack`` being used explicitly in older versions
1342+
of Python, where ``*`` couldn't be used in certain places::
1343+
1344+
# In older versions of Python, TypeVarTuple and Unpack
1345+
# are located in the `typing_extensions` backports package.
1346+
from typing_extensions import TypeVarTuple, Unpack
1347+
1348+
Ts = TypeVarTuple('Ts')
1349+
tup: tuple[*Ts] # Syntax error on Python <= 3.10!
1350+
tup: tuple[Unpack[Ts]] # Semantically equivalent, and backwards-compatible
1351+
12331352
.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)
12341353

12351354
Parameter specification variable. A specialized version of

0 commit comments

Comments
 (0)