From f5d5c14e45225aba4ddf3f32722162f419e6497a Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Thu, 24 Mar 2022 21:55:18 +0000 Subject: [PATCH 01/14] Add documentation for TypeVarTuple --- Doc/library/typing.rst | 79 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 4d833dc497f177..ee8ed48e7c8e3c 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,83 @@ 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 + + :class:`Type variable ` tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + Shape = TypeVarTuple('Shape') + + Vanilla type variables enable parameterization with a *fixed* number of + types. Type variable tuples, on the other hand, allow parameterization with + an *arbitrary* number of types by acting like an *arbitrary* number of type + variables wrapped in a tuple. For example:: + + class Array(Generic[*Shape]): + pass + + from typing import NewType + Height = NewType('Height', int) + Width = NewType('Width', int) + + 1d_array: Array[Height] = Array() # This is fine + + With one type argument, ``Array`` behaves as if ``Shape = (T,)``, where + ``T`` is a type variable. That means ``Array`` behaves as if we'd written + ``Generic[T]``. Note the use of the unpacking operator ``*`` to bring ``T`` + out of the tuple and directly into the square brackets. + + :: + + 2d_array: Array[Height, Width] = Array() # This is also fine! + + With two arguments, it's as if ``Shape = (T1, T2)``, where ``T1`` and ``T2`` + are type variables; as if ``Array`` was defined with ``Generic[T1, T2]``. + + Type variable tuples can be used any place a vanilla type variable can. + However, note that they must always be used in combination with the + unpacking operator ``*``. This helps distinguish type variable tuples from + normal type variables:: + + def returns_tuple() -> Ts: # Not valid + return ('spam', 42) + + def returns_tuple() -> tuple[*Ts]: # This is fine :) + return ('spam', 42) + + Type variable tuples can be happily combined with vanilla type variables:: + + DType = TypeVar('DType') + + class Array(Generic[DType, *Shape]): # This is fine + pass + + 1d_float_array: Array[float, Height] = Array() # Totally fine + 2d_int_array: 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:: + + class Array(Generic[*Shape, *Shape]): # Not valid + pass + + Finally, an unpacked type variable tuple can be used as the type annotation + of ``*args``:: + + def args_to_tuple(*args: *Ts) -> tuple[*Ts]: + return tuple(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``. + In this case, it binds those types to ``Ts``. + + For more details on type variable tuples, see :pep:`646`. + + .. versionadded:: 3.11 + .. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False) Parameter specification variable. A specialized version of From 34111b62337330baba32739902400401bf664332 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 26 Mar 2022 10:44:18 +0000 Subject: [PATCH 02/14] Address Jelle and Guido's feedback on TypeVarTuple --- Doc/library/typing.rst | 64 ++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index ee8ed48e7c8e3c..08f8b34394f960 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1236,47 +1236,49 @@ These are not used in annotations. They are building blocks for creating generic :class:`Type variable ` tuple. - Usage:: + A vanilla 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:: - Ts = TypeVarTuple('Ts') Shape = TypeVarTuple('Shape') - Vanilla type variables enable parameterization with a *fixed* number of - types. Type variable tuples, on the other hand, allow parameterization with - an *arbitrary* number of types by acting like an *arbitrary* number of type - variables wrapped in a tuple. For example:: + class Array(Generic[*Shape]): ... - class Array(Generic[*Shape]): - pass - - from typing import NewType - Height = NewType('Height', int) - Width = NewType('Width', int) + array_1d: Array[int] = Array() # 1 type parameter: fine + array_2d: Array[int, int] = Array() # 2 type parameters: also fine + array_0d: Array[()] = Array() # 0 type parameters: also fine - 1d_array: Array[Height] = Array() # This is fine + Note the use of the unpacking operator ``*`` in ``Generic[*Shape]``. + Conceptually, you can think of ``Shape`` as a tuple of type variables + ``(T1, T2, ...)``. ``Generic[*Shape]`` would then become + ``Generic[*(T1, T2, ...)]``. The ``*`` brings the type variables out of the + tuple and into the square brackets directly. - With one type argument, ``Array`` behaves as if ``Shape = (T,)``, where - ``T`` is a type variable. That means ``Array`` behaves as if we'd written - ``Generic[T]``. Note the use of the unpacking operator ``*`` to bring ``T`` - out of the tuple and directly into the square brackets. + (In older versions of Python, where ``*`` was less flexible, you might see + this written as ``Unpack[Shape]`` instead. This means the same thing as + ``*Shape`` - ``Unpack`` and ``*`` can be used interchangeably.) - :: + Type variable tuples must *always* be used in combination with the unpacking + operator. This helps distinguish type variable types from normal type + variables:: - 2d_array: Array[Height, Width] = Array() # This is also fine! + class Array(Generic[Shape]): ... # Not valid - With two arguments, it's as if ``Shape = (T1, T2)``, where ``T1`` and ``T2`` - are type variables; as if ``Array`` was defined with ``Generic[T1, T2]``. + In cases where you really do just want a tuple of types, make this explicit + by wrapping the type variable tuple in ``tuple[]``:: - Type variable tuples can be used any place a vanilla type variable can. - However, note that they must always be used in combination with the - unpacking operator ``*``. This helps distinguish type variable tuples from - normal type variables:: + shape: Shape # Not valid + shape: tuple[Shape] # Also not valid + shape: tuple[*Shape] # The correct way to do it - def returns_tuple() -> Ts: # Not valid - return ('spam', 42) + Type variable tuples can be used in the same contexts as vanilla type + variables. For example, in class definitions, arguments, and return types:: - def returns_tuple() -> tuple[*Ts]: # This is fine :) - return ('spam', 42) + 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 vanilla type variables:: @@ -1285,8 +1287,8 @@ These are not used in annotations. They are building blocks for creating generic class Array(Generic[DType, *Shape]): # This is fine pass - 1d_float_array: Array[float, Height] = Array() # Totally fine - 2d_int_array: Array[int, Height, Width] = Array() # Yup, fine too + 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:: From 3802fe2c2e6a3aad958924fa50649142d0492a68 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 26 Mar 2022 11:13:51 +0000 Subject: [PATCH 03/14] Add short section on Unpack --- Doc/library/typing.rst | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 08f8b34394f960..b874aec763f14a 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1256,8 +1256,10 @@ These are not used in annotations. They are building blocks for creating generic tuple and into the square brackets directly. (In older versions of Python, where ``*`` was less flexible, you might see - this written as ``Unpack[Shape]`` instead. This means the same thing as - ``*Shape`` - ``Unpack`` and ``*`` can be used interchangeably.) + this written using :data:`Unpack ` instead, as + ``Unpack[Shape]``. This means the same thing as ``*Shape`` - ``Unpack`` and + ``*`` can be used interchangeably in the context of types.) + Type variable tuples must *always* be used in combination with the unpacking operator. This helps distinguish type variable types from normal type @@ -1311,6 +1313,22 @@ These are not used in annotations. They are building blocks for creating generic .. 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 ` internally uses ``Unpack`` + to mark the type variable tuple as having been unpacked:: + + Ts = TypeVarTuple('Ts') + array: tuple[*Ts] + # Effectively does: + array: 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. + .. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False) Parameter specification variable. A specialized version of From c755600b02897f17b39454d9f0c78ad0ae823f23 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Wed, 30 Mar 2022 20:14:13 +0100 Subject: [PATCH 04/14] Explicit mention Generic[*Shape, DType] as fine --- Doc/library/typing.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index b874aec763f14a..5303179811b06d 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1289,6 +1289,9 @@ These are not used in annotations. They are building blocks for creating generic 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 From b97827cf99edbc6c204ff754639351e6d1797a9a Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Wed, 30 Mar 2022 20:39:12 +0100 Subject: [PATCH 05/14] Incorporate Alex's feedback --- Doc/library/typing.rst | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 5303179811b06d..d9a7c958b034bb 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1234,9 +1234,10 @@ These are not used in annotations. They are building blocks for creating generic .. class:: TypeVarTuple - :class:`Type variable ` tuple. + Type variable tuple. A specialised form of :class:`Type variable ` + that enables *variadic* generics. - A vanilla type variable enables parameterization with a single type. A type + 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:: @@ -1252,20 +1253,15 @@ These are not used in annotations. They are building blocks for creating generic Note the use of the unpacking operator ``*`` in ``Generic[*Shape]``. Conceptually, you can think of ``Shape`` as a tuple of type variables ``(T1, T2, ...)``. ``Generic[*Shape]`` would then become - ``Generic[*(T1, T2, ...)]``. The ``*`` brings the type variables out of the - tuple and into the square brackets directly. + ``Generic[*(T1, T2, ...)]``, which is equivalent to + ``Generic[T1, T2, ...]``. (Note that in older versions of Python, you might + see this written using :data:`Unpack ` instead, as + ``Unpack[Shape]``.) - (In older versions of Python, where ``*`` was less flexible, you might see - this written using :data:`Unpack ` instead, as - ``Unpack[Shape]``. This means the same thing as ``*Shape`` - ``Unpack`` and - ``*`` can be used interchangeably in the context of types.) + Type variable tuples must *always* be unpacked. This helps distinguish type + variable types from normal type variables:: - - Type variable tuples must *always* be used in combination with the unpacking - operator. This helps distinguish type variable types from normal type - variables:: - - class Array(Generic[Shape]): ... # Not valid + class C(Generic[Shape]): ... # Not valid (should be Generic[*Shape]) In cases where you really do just want a tuple of types, make this explicit by wrapping the type variable tuple in ``tuple[]``:: @@ -1274,7 +1270,7 @@ These are not used in annotations. They are building blocks for creating generic shape: tuple[Shape] # Also not valid shape: tuple[*Shape] # The correct way to do it - Type variable tuples can be used in the same contexts as vanilla type + Type variable tuples can be used in the same contexts as normal type variables. For example, in class definitions, arguments, and return types:: class Array(Generic[*Shape]): @@ -1282,7 +1278,7 @@ These are not used in annotations. They are building blocks for creating generic def __abs__(self) -> Array[*Shape]: ... def get_shape(self) -> tuple[*Shape]: ... - Type variable tuples can be happily combined with vanilla type variables:: + Type variable tuples can be happily combined with normal type variables:: DType = TypeVar('DType') @@ -1324,13 +1320,19 @@ These are not used in annotations. They are building blocks for creating generic to mark the type variable tuple as having been unpacked:: Ts = TypeVarTuple('Ts') - array: tuple[*Ts] + tup: tuple[*Ts] # Effectively does: - array: tuple[Unpack[Ts]] + 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. + of Python, where ``*`` couldn't be used in certain places:: + + 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) From 5362e0377647dd8d2536adb0c78ce96c0a75bb31 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 2 Apr 2022 10:30:35 +0100 Subject: [PATCH 06/14] Switch initial example to remove_first_element --- Doc/library/typing.rst | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index d9a7c958b034bb..60d88de30e31ae 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1242,33 +1242,37 @@ These are not used in annotations. They are building blocks for creating generic *arbitrary* number of types by acting like an *arbitrary* number of type variables wrapped in a tuple. For example:: - Shape = TypeVarTuple('Shape') + T = TypeVar('T') + Ts = TypeVarTuple('Ts') + + def remove_first_element(xs: tuple[T, *Ts]) -> tuple[*Ts]: ... + + remove_first_element(xs=(1,)) + # T is bound to int, Ts is bound to () + # Return type is tuple[()] - class Array(Generic[*Shape]): ... + remove_first_element(xs=(1, '2')) + # T is bound to int, Ts is bound to (str,) + # Return type is tuple[str] - array_1d: Array[int] = Array() # 1 type parameter: fine - array_2d: Array[int, int] = Array() # 2 type parameters: also fine - array_0d: Array[()] = Array() # 0 type parameters: also fine + remove_first_element(xs=(1, '2', 3.0)) + # T is bound to int, Ts is bound to (str, float) + # Return type is tuple[str, float] - Note the use of the unpacking operator ``*`` in ``Generic[*Shape]``. - Conceptually, you can think of ``Shape`` as a tuple of type variables - ``(T1, T2, ...)``. ``Generic[*Shape]`` would then become - ``Generic[*(T1, T2, ...)]``, which is equivalent to - ``Generic[T1, T2, ...]``. (Note that in older versions of Python, you might + 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 ` instead, as - ``Unpack[Shape]``.) + ``Unpack[Ts]``.) Type variable tuples must *always* be unpacked. This helps distinguish type variable types from normal type variables:: - class C(Generic[Shape]): ... # Not valid (should be Generic[*Shape]) - - In cases where you really do just want a tuple of types, make this explicit - by wrapping the type variable tuple in ``tuple[]``:: - - shape: Shape # Not valid - shape: tuple[Shape] # Also not valid - shape: tuple[*Shape] # The correct way to do it + 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:: From c0eb4ce0cadf51afe7dc7ca977b33259f8d1bb5e Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 2 Apr 2022 10:35:58 +0100 Subject: [PATCH 07/14] Switch to call_soon for *args: *Ts example --- Doc/library/typing.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 60d88de30e31ae..f910566ec59aa6 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1277,6 +1277,7 @@ These are not used in annotations. They are building blocks for creating generic 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]: ... @@ -1304,13 +1305,19 @@ These are not used in annotations. They are building blocks for creating generic Finally, an unpacked type variable tuple can be used as the type annotation of ``*args``:: - def args_to_tuple(*args: *Ts) -> tuple[*Ts]: - return tuple(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``. - In this case, it binds those types to ``Ts``. + 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`. From 3bc701e0cc22347192c609cd6ccf46b6d527d7db Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 2 Apr 2022 10:36:34 +0100 Subject: [PATCH 08/14] Explicitly state the tuple[*Ts, *Ts] is invalid too --- Doc/library/typing.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index f910566ec59aa6..19e69d816cd6cb 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1299,6 +1299,7 @@ These are not used in annotations. They are building blocks for creating generic 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 From 896f4f43199ed06eab85cf5c780b6863bc9ae038 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 2 Apr 2022 11:20:22 +0100 Subject: [PATCH 09/14] Expand remove_first_element example Co-authored-by: Alex Waygood --- Doc/library/typing.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 19e69d816cd6cb..b3116fc0b08f70 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1245,19 +1245,20 @@ These are not used in annotations. They are building blocks for creating generic T = TypeVar('T') Ts = TypeVarTuple('Ts') - def remove_first_element(xs: tuple[T, *Ts]) -> tuple[*Ts]: ... + def remove_first_element(xs: tuple[T, *Ts]) -> tuple[*Ts]: + return xs[1:] remove_first_element(xs=(1,)) # T is bound to int, Ts is bound to () - # Return type is tuple[()] + # Return value is (), which has type tuple[()] - remove_first_element(xs=(1, '2')) + remove_first_element(xs=(1, 'spam')) # T is bound to int, Ts is bound to (str,) - # Return type is tuple[str] + # Return value is ('foo',), which has type tuple[str] - remove_first_element(xs=(1, '2', 3.0)) + remove_first_element(xs=(1, 'spam', 3.0)) # T is bound to int, Ts is bound to (str, float) - # Return type is tuple[str, float] + # Return value is ('spam', 3.0), which has type tuple[str, float] Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``. Conceptually, you can think of ``Ts`` as a tuple of type variables From 5d67004191b837198a34a151cecb99340e0eb094 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 2 Apr 2022 11:30:26 +0100 Subject: [PATCH 10/14] Add note about why typing_extensions --- Doc/library/typing.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index b3116fc0b08f70..9736f09279d47e 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1341,6 +1341,8 @@ These are not used in annotations. They are building blocks for creating generic 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') From 29e7a262db781152513f5aebf25a94d66ea532cc Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 2 Apr 2022 11:34:58 +0100 Subject: [PATCH 11/14] Make explanation comment consistent Co-authored-by: Alex Waygood --- Doc/library/typing.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 9736f09279d47e..b5128f970d53ba 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1248,17 +1248,17 @@ These are not used in annotations. They are building blocks for creating generic def remove_first_element(xs: tuple[T, *Ts]) -> tuple[*Ts]: return xs[1:] - remove_first_element(xs=(1,)) # T is bound to int, Ts is bound to () # Return value is (), which has type tuple[()] + remove_first_element(xs=(1,)) - remove_first_element(xs=(1, 'spam')) # T is bound to int, Ts is bound to (str,) # Return value is ('foo',), which has type tuple[str] + remove_first_element(xs=(1, 'spam')) - remove_first_element(xs=(1, 'spam', 3.0)) # 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(xs=(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 From 3e0fcb77813a149f4fbf79913fc9147e3a62672d Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 2 Apr 2022 11:41:49 +0100 Subject: [PATCH 12/14] Fix typo Co-authored-by: Alex Waygood --- Doc/library/typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index b5128f970d53ba..0fc6befbfe2984 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1253,7 +1253,7 @@ These are not used in annotations. They are building blocks for creating generic remove_first_element(xs=(1,)) # T is bound to int, Ts is bound to (str,) - # Return value is ('foo',), which has type tuple[str] + # Return value is ('spam',), which has type tuple[str] remove_first_element(xs=(1, 'spam')) # T is bound to int, Ts is bound to (str, float) From c346065c500ad4eff34f39eeca8c46fe97b81899 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 2 Apr 2022 11:45:52 +0100 Subject: [PATCH 13/14] Replace arg name 'xs' with 'tup' --- Doc/library/typing.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 0fc6befbfe2984..6825f395760865 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1245,20 +1245,20 @@ These are not used in annotations. They are building blocks for creating generic T = TypeVar('T') Ts = TypeVarTuple('Ts') - def remove_first_element(xs: tuple[T, *Ts]) -> tuple[*Ts]: - return xs[1:] + 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(xs=(1,)) + 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(xs=(1, 'spam')) + 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(xs=(1, 'spam', 3.0)) + 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 From 668017764ab217bd1b82a22517854a44609c762b Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Mon, 4 Apr 2022 23:56:40 +0100 Subject: [PATCH 14/14] Fix final nits Co-authored-by: Jelle Zijlstra --- Doc/library/typing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 6825f395760865..37c17c429fa47c 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1234,7 +1234,7 @@ These are not used in annotations. They are building blocks for creating generic .. class:: TypeVarTuple - Type variable tuple. A specialised form of :class:`Type variable ` + Type variable tuple. A specialized form of :class:`Type variable ` that enables *variadic* generics. A normal type variable enables parameterization with a single type. A type @@ -1329,7 +1329,7 @@ These are not used in annotations. They are building blocks for creating generic A typing operator that conceptually marks an object as having been unpacked. For example, using the unpack operator ``*`` on a - :class:`type variable tuple ` internally uses ``Unpack`` + :class:`type variable tuple ` is equivalent to using ``Unpack`` to mark the type variable tuple as having been unpacked:: Ts = TypeVarTuple('Ts')