-
Notifications
You must be signed in to change notification settings - Fork 262
Added draft chapter to typing spec for tuples. #1599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
51ff533
Added draft chapter to typing spec for tuples. This consolidates and …
erictraut e373264
Added suggested text from @srittau that clarifies illegal forms of un…
erictraut 9b48ff9
Incorporated PR feedback from Jelle.
erictraut 1ba2532
Incorporated feedback on `Sequence` type compatibility section.
erictraut 7a33c93
Incorporated PR feedback from @rchen152.
erictraut 671fe43
Fixed minor grammatical error.
erictraut c7fb43b
Incorporated PR feedback from @hauntsaninja.
erictraut 7b9abcb
Merge branch 'main' into tuples_spec
erictraut File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
Tuples | ||
====== | ||
|
||
The ``tuple`` class has some special behaviors and properties that make it | ||
different from other classes from a typing perspective. The most obvious | ||
difference is that ``tuple`` is variadic -- it supports an arbitrary number | ||
of type arguments. At runtime, the sequence of objects contained within the | ||
tuple is fixed at the time of construction. Elements cannot be added, removed, | ||
reordered, or replaced after construction. These properties affect subtyping | ||
rules and other behaviors as described below. | ||
|
||
|
||
Tuple Type Form | ||
--------------- | ||
|
||
The type of a tuple can be expressed by listing the element types. For | ||
example, ``tuple[int, int, str]`` is a tuple containing an ``int``, another | ||
``int``, and a ``str``. | ||
|
||
The empty tuple can be annotated as ``tuple[()]``. | ||
|
||
Arbitrary-length homogeneous tuples can be expressed using one type and an | ||
ellipsis, for example ``tuple[int, ...]``. This type is equivalent to a union | ||
erictraut marked this conversation as resolved.
Show resolved
Hide resolved
|
||
of tuples containing zero or more ``int`` elements (``tuple[()] | | ||
tuple[int] | tuple[int, int] | tuple[int, int, int] | ...``). | ||
Arbitrary-length homogeneous tuples are sometimes referred to as "unbounded | ||
tuples". Both of these terms appear within the typing spec, and they refer to | ||
erictraut marked this conversation as resolved.
Show resolved
Hide resolved
|
||
the same concept. | ||
|
||
The type ``tuple[Any, ...]`` is special in that it is bidirectionally | ||
erictraut marked this conversation as resolved.
Show resolved
Hide resolved
|
||
compatible with any tuple of any length. This is useful for gradual typing. | ||
The type ``tuple`` (with no type arguments provided) is equivalent to | ||
``tuple[Any, ...]``. | ||
|
||
Arbitrary-length tuples have exactly two type arguments -- the type and | ||
an ellipsis. Any other tuple form that uses an ellipsis is invalid:: | ||
|
||
t1: tuple[int, ...] # OK | ||
t2: tuple[int, int, ...] # Invalid | ||
t3: tuple[...] # Invalid | ||
t4: tuple[..., int] # Invalid | ||
t5: tuple[int, ..., int] # Invalid | ||
t6: tuple[*tuple[str], ...] # Invalid | ||
t7: tuple[*tuple[str, ...], ...] # Invalid | ||
|
||
|
||
Unpacked Tuple Form | ||
------------------- | ||
|
||
An unpacked form of ``tuple`` (using an unpack operator ``*``) can be used | ||
within a tuple type argument list. For example, ``tuple[int, *tuple[str]]`` | ||
is equivalent to ``tuple[int, str]``. Unpacking an unbounded tuple preserves | ||
the unbounded tuple as it is. That is, ``*tuple[int, ...]`` remains | ||
``*tuple[int, ...]``; there's no simpler form. This enables us to specify | ||
types such as ``tuple[int, *tuple[str, ...], str]`` -- a tuple type where the | ||
first element is guaranteed to be of type ``int``, the last element is | ||
guaranteed to be of type ``str``, and the elements in the middle are zero or | ||
more elements of type ``str``. The type ``tuple[*tuple[int, ...]]`` is | ||
equivalent to ``tuple[int, ...]``. | ||
|
||
If an unpacked ``*tuple[Any, ...]`` is embedded within another tuple, that | ||
portion of the tuple is bidirectionally type compatible with any tuple of | ||
any length. | ||
|
||
Only one unbounded tuple can be used within another tuple:: | ||
|
||
t1: tuple[*tuple[str], *tuple[str]] # OK | ||
t2: tuple[*tuple[str, *tuple[str, ...]]] # OK | ||
t3: tuple[*tuple[str, ...], *tuple[int, ...]] # Type error | ||
t4: tuple[*tuple[str, *tuple[str, ...]], *tuple[int, ...]] # Type error | ||
|
||
An unpacked TypeVarTuple counts as an unbounded tuple in the context of this rule:: | ||
|
||
def func[*Ts](t: tuple[*Ts]): | ||
t5: tuple[*tuple[str], *Ts] # OK | ||
t6: tuple[*tuple[str, ...], *Ts] # Type error | ||
|
||
The ``*`` syntax requires Python 3.11 or newer. For older versions of Python, | ||
the ``typing.Unpack`` special form can be used: | ||
``tuple[int, Unpack[tuple[str, ...]], int]``. | ||
|
||
Unpacked tuples can also be used for ``*args`` parameters in a function | ||
signature: ``def f(*args: *tuple[int, str]): ...``. Unpacked tuples | ||
can also be used for specializing generic classes or type variables that are | ||
parameterized using a ``TypeVarTuple``. For more details, see | ||
:ref:`args_as_typevartuple`. | ||
|
||
|
||
Type Compatibility Rules | ||
------------------------ | ||
|
||
Because tuple contents are immutable, the element types of a tuple are covariant. | ||
For example, ``tuple[int, int]`` is a subtype of ``tuple[float, complex]``. | ||
|
||
As discussed above, a homogeneous tuple of arbitrary length is equivalent | ||
to a union of tuples of different lengths. That means ``tuple[()]``, | ||
``tuple[int]`` and ``tuple[int, *tuple[int, ...]]`` are all subtypes of | ||
``tuple[int, ...]``. The converse is not true; ``tuple[int, ...]``` is not a | ||
subtype of ``tuple[int]``. | ||
|
||
The type ``tuple[Any, ...]`` is bidirectionally compatible with any tuple:: | ||
|
||
def func(t1: tuple[int], t2: tuple[int, ...], t3: tuple[Any, ...]): | ||
v1: tuple[int, ...] = t1 # OK | ||
v2: tuple[Any, ...] = t1 # OK | ||
|
||
v3: tuple[int] = t2 # Type error | ||
v4: tuple[Any, ...] = t2 # OK | ||
|
||
v5: tuple[float, float] = t3 # OK | ||
v6: tuple[int, *tuple[str, ...]] = t3 # OK | ||
|
||
|
||
The length of a tuple at runtime is immutable, so it is safe for type checkers | ||
to use length checks to narrow the type of a tuple:: | ||
|
||
def func(val: tuple[int] | tuple[str, str] | tuple[int, *tuple[str], int]): | ||
erictraut marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(val) == 1: | ||
# Type can be narrowed to tuple[int]. | ||
reveal_type(val) # tuple[int] | ||
|
||
if len(val) == 2: | ||
# Type can be narrowed to tuple[str, str] | tuple[int, int]. | ||
erictraut marked this conversation as resolved.
Show resolved
Hide resolved
|
||
reveal_type(val) # tuple[str, str] | tuple[int, int] | ||
|
||
if len(val) == 3: | ||
# Type can be narrowed to tuple[int, str, int]. | ||
reveal_type(val) # tuple[int, str, int] | ||
|
||
This property may also be used to safely narrow tuple types within a ``match`` | ||
statement that uses sequence patterns. | ||
|
||
If a tuple element is a union type, the tuple can be safely expanded into a | ||
union of tuples. For example, ``tuple[int | str]`` is equivalent to | ||
``tuple[int] | tuple[str]``. If multiple elements are union types, the expansion | ||
must consider all combinations. For example, ``tuple[int | str, int | str]`` is | ||
erictraut marked this conversation as resolved.
Show resolved
Hide resolved
|
||
equivalent to ``tuple[int, int] | tuple[int, str] | tuple[str, int] | tuple[str, str]``. | ||
Unbounded tuples cannot be expanded in this manner. | ||
|
||
Type checkers may safely use this equivalency rule when narrowing tuple types:: | ||
|
||
def func(subj: tuple[int | str, int | str]): | ||
match subj: | ||
case x, str(): | ||
reveal_type(subj) # tuple[int | str, str] | ||
case y: | ||
reveal_type(subj) # tuple[int | str, int] | ||
|
||
The ``tuple`` class derives from ``Sequence[T_co]`` where ``T_co`` is a covariant | ||
(non-variadic) type variable. The specialized type of ``T_co`` should be computed | ||
by a type checker as a a supertype of all element types. | ||
For example, ``tuple[int, *tuple[str, ...]]`` is a subtype of | ||
``Sequence[int | str]`` or ``Sequence[object]``. | ||
|
||
A zero-length tuple (``tuple[()]``) is a subtype of ``Sequence[Never]``. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.