[3.11] gh-103921: Improve typing documentation (GH-104642) (#105007)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Jelle Zijlstra 2023-05-27 16:30:41 -07:00 committed by GitHub
parent dcfa8165ad
commit d34e58a1d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 190 additions and 158 deletions

View file

@ -136,6 +136,13 @@ Type aliases are useful for simplifying complex type signatures. For example::
Note that ``None`` as a type hint is a special case and is replaced by
``type(None)``.
Type aliases may be marked with :data:`TypeAlias` to make it explicit that
the statement is a type alias declaration, not a normal variable assignment::
from typing import TypeAlias
Vector: TypeAlias = list[float]
.. _distinct:
NewType
@ -367,15 +374,15 @@ You can use multiple inheritance with :class:`Generic`::
class LinkedList(Sized, Generic[T]):
...
When inheriting from generic classes, some type variables could be fixed::
When inheriting from generic classes, some type parameters could be fixed::
from collections.abc import Mapping
from typing import TypeVar
from collections.abc import Mapping
from typing import TypeVar
T = TypeVar('T')
T = TypeVar('T')
class MyDict(Mapping[str, T]):
...
class MyDict(Mapping[str, T]):
...
In this case ``MyDict`` has a single parameter, ``T``.
@ -387,7 +394,7 @@ not generic but implicitly inherits from ``Iterable[Any]``::
class MyIterable(Iterable): # Same as Iterable[Any]
User defined generic type aliases are also supported. Examples::
User-defined generic type aliases are also supported. Examples::
from collections.abc import Iterable
from typing import TypeVar
@ -423,7 +430,6 @@ to this is that a list of types can be used to substitute a :class:`ParamSpec`::
>>> Z[int, [dict, float]]
__main__.Z[int, (<class 'dict'>, <class 'float'>)]
Furthermore, a generic with only one parameter specification variable will accept
parameter lists in the forms ``X[[Type1, Type2, ...]]`` and also
``X[Type1, Type2, ...]`` for aesthetic reasons. Internally, the latter is converted
@ -665,20 +671,20 @@ These can be used as types in annotations and do not support ``[]``.
This can be used to define a function that should never be
called, or a function that never returns::
from typing import Never
from typing import Never
def never_call_me(arg: Never) -> None:
pass
def never_call_me(arg: Never) -> None:
pass
def int_or_str(arg: int | str) -> None:
never_call_me(arg) # type checker error
match arg:
case int():
print("It's an int")
case str():
print("It's a str")
case _:
never_call_me(arg) # ok, arg is of type Never
def int_or_str(arg: int | str) -> None:
never_call_me(arg) # type checker error
match arg:
case int():
print("It's an int")
case str():
print("It's a str")
case _:
never_call_me(arg) # ok, arg is of type Never
.. versionadded:: 3.11
@ -712,9 +718,9 @@ These can be used as types in annotations and do not support ``[]``.
from typing import Self
class Foo:
def return_self(self) -> Self:
...
return self
def return_self(self) -> Self:
...
return self
This annotation is semantically equivalent to the following,
@ -725,16 +731,16 @@ These can be used as types in annotations and do not support ``[]``.
Self = TypeVar("Self", bound="Foo")
class Foo:
def return_self(self: Self) -> Self:
...
return self
def return_self(self: Self) -> Self:
...
return self
In general if something currently follows the pattern of::
class Foo:
def return_self(self) -> "Foo":
...
return self
def return_self(self) -> "Foo":
...
return self
You should use :data:`Self` as calls to ``SubclassOfFoo.return_self`` would have
``Foo`` as the return type and not ``SubclassOfFoo``.
@ -1249,7 +1255,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn
Building generic types
""""""""""""""""""""""
These are not used in annotations. They are building blocks for creating generic types.
The following objects are not used directly in annotations. Instead, they are building blocks
for creating generic types.
.. class:: Generic
@ -1275,177 +1282,204 @@ These are not used in annotations. They are building blocks for creating generic
except KeyError:
return default
.. class:: TypeVar
.. class:: TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False)
Type variable.
Type variable.
Usage::
Usage::
T = TypeVar('T') # Can be anything
S = TypeVar('S', bound=str) # Can be any subtype of str
A = TypeVar('A', str, bytes) # Must be exactly str or bytes
Type variables exist primarily for the benefit of static type
checkers. They serve as the parameters for generic types as well
as for generic function definitions. See :class:`Generic` for more
information on generic types. Generic functions work as follows::
Type variables exist primarily for the benefit of static type
checkers. They serve as the parameters for generic types as well
as for generic function and type alias definitions.
See :class:`Generic` for more
information on generic types. Generic functions work as follows::
def repeat(x: T, n: int) -> Sequence[T]:
"""Return a list containing n references to x."""
return [x]*n
def repeat(x: T, n: int) -> Sequence[T]:
"""Return a list containing n references to x."""
return [x]*n
def print_capitalized(x: S) -> S:
"""Print x capitalized, and return x."""
print(x.capitalize())
return x
def print_capitalized(x: S) -> S:
"""Print x capitalized, and return x."""
print(x.capitalize())
return x
def concatenate(x: A, y: A) -> A:
"""Add two strings or bytes objects together."""
return x + y
def concatenate(x: A, y: A) -> A:
"""Add two strings or bytes objects together."""
return x + y
Note that type variables can be *bound*, *constrained*, or neither, but
cannot be both bound *and* constrained.
Note that type variables can be *bound*, *constrained*, or neither, but
cannot be both bound *and* constrained.
Bound type variables and constrained type variables have different
semantics in several important ways. Using a *bound* type variable means
that the ``TypeVar`` will be solved using the most specific type possible::
Created type variables may be explicitly marked covariant or contravariant by passing
``covariant=True`` or ``contravariant=True``.
By default, type variables are invariant.
See :pep:`484` and :pep:`695` for more details.
x = print_capitalized('a string')
reveal_type(x) # revealed type is str
Bound type variables and constrained type variables have different
semantics in several important ways. Using a *bound* type variable means
that the ``TypeVar`` will be solved using the most specific type possible::
class StringSubclass(str):
pass
x = print_capitalized('a string')
reveal_type(x) # revealed type is str
y = print_capitalized(StringSubclass('another string'))
reveal_type(y) # revealed type is StringSubclass
class StringSubclass(str):
pass
z = print_capitalized(45) # error: int is not a subtype of str
y = print_capitalized(StringSubclass('another string'))
reveal_type(y) # revealed type is StringSubclass
Type variables can be bound to concrete types, abstract types (ABCs or
protocols), and even unions of types::
z = print_capitalized(45) # error: int is not a subtype of str
U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes
V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method
Type variables can be bound to concrete types, abstract types (ABCs or
protocols), and even unions of types::
.. _typing-constrained-typevar:
U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes
V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method
Using a *constrained* type variable, however, means that the ``TypeVar``
can only ever be solved as being exactly one of the constraints given::
.. _typing-constrained-typevar:
a = concatenate('one', 'two')
reveal_type(a) # revealed type is str
Using a *constrained* type variable, however, means that the ``TypeVar``
can only ever be solved as being exactly one of the constraints given::
b = concatenate(StringSubclass('one'), StringSubclass('two'))
reveal_type(b) # revealed type is str, despite StringSubclass being passed in
a = concatenate('one', 'two')
reveal_type(a) # revealed type is str
c = concatenate('one', b'two') # error: type variable 'A' can be either str or bytes in a function call, but not both
b = concatenate(StringSubclass('one'), StringSubclass('two'))
reveal_type(b) # revealed type is str, despite StringSubclass being passed in
At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`. In general,
:func:`isinstance` and :func:`issubclass` should not be used with types.
c = concatenate('one', b'two') # error: type variable 'A' can be either str or bytes in a function call, but not both
Type variables may be marked covariant or contravariant by passing
``covariant=True`` or ``contravariant=True``. See :pep:`484` for more
details. By default, type variables are invariant.
At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`.
.. class:: TypeVarTuple
.. attribute:: __name__
Type variable tuple. A specialized form of :class:`type variable <TypeVar>`
that enables *variadic* generics.
The name of the type variable.
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::
.. attribute:: __covariant__
T = TypeVar('T')
Ts = TypeVarTuple('Ts')
Whether the type var has been marked as covariant.
def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
return (*tup[1:], tup[0])
.. attribute:: __contravariant__
# T is bound to int, Ts is bound to ()
# Return value is (1,), which has type tuple[int]
move_first_element_to_last(tup=(1,))
Whether the type var has been marked as contravariant.
# T is bound to int, Ts is bound to (str,)
# Return value is ('spam', 1), which has type tuple[str, int]
move_first_element_to_last(tup=(1, 'spam'))
.. attribute:: __bound__
# T is bound to int, Ts is bound to (str, float)
# Return value is ('spam', 3.0, 1), which has type tuple[str, float, int]
move_first_element_to_last(tup=(1, 'spam', 3.0))
The bound of the type variable, if any.
# This fails to type check (and fails at runtime)
# because tuple[()] is not compatible with tuple[T, *Ts]
# (at least one element is required)
move_first_element_to_last(tup=())
.. attribute:: __constraints__
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]``.)
A tuple containing the constraints of the type variable, if any.
Type variable tuples must *always* be unpacked. This helps distinguish type
variable tuples from normal type variables::
.. class:: TypeVarTuple(name)
x: Ts # Not valid
x: tuple[Ts] # Not valid
x: tuple[*Ts] # The correct way to do it
Type variable tuple. A specialized form of :class:`type variable <TypeVar>`
that enables *variadic* generics.
Type variable tuples can be used in the same contexts as normal type
variables. For example, in class definitions, arguments, and return types::
Usage::
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]: ...
T = TypeVar("T")
Ts = TypeVarTuple("Ts")
Type variable tuples can be happily combined with normal type variables::
def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
return (*tup[1:], tup[0])
DType = TypeVar('DType')
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::
class Array(Generic[DType, *Shape]): # This is fine
pass
# T is bound to int, Ts is bound to ()
# Return value is (1,), which has type tuple[int]
move_first_element_to_last(tup=(1,))
class Array2(Generic[*Shape, DType]): # This would also be fine
pass
# T is bound to int, Ts is bound to (str,)
# Return value is ('spam', 1), which has type tuple[str, int]
move_first_element_to_last(tup=(1, 'spam'))
float_array_1d: Array[float, Height] = Array() # Totally fine
int_array_2d: Array[int, Height, Width] = Array() # Yup, fine too
# T is bound to int, Ts is bound to (str, float)
# Return value is ('spam', 3.0, 1), which has type tuple[str, float, int]
move_first_element_to_last(tup=(1, 'spam', 3.0))
However, note that at most one type variable tuple may appear in a single
list of type arguments or type parameters::
# This fails to type check (and fails at runtime)
# because tuple[()] is not compatible with tuple[T, *Ts]
# (at least one element is required)
move_first_element_to_last(tup=())
x: tuple[*Ts, *Ts] # Not valid
class Array(Generic[*Shape, *Shape]): # Not valid
pass
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]``.)
Finally, an unpacked type variable tuple can be used as the type annotation
of ``*args``::
Type variable tuples must *always* be unpacked. This helps distinguish type
variable tuples from normal type variables::
def call_soon(
callback: Callable[[*Ts], None],
*args: *Ts
) -> None:
...
callback(*args)
x: Ts # Not valid
x: tuple[Ts] # Not valid
x: tuple[*Ts] # The correct way to do it
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``.
Type variable tuples can be used in the same contexts as normal type
variables. For example, in class definitions, arguments, and return types::
See :pep:`646` for more details on type variable tuples.
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]: ...
.. versionadded:: 3.11
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``.
See :pep:`646` for more details on type variable tuples.
.. attribute:: __name__
The name of the type variable tuple.
.. versionadded:: 3.11
.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)
@ -1510,6 +1544,10 @@ These are not used in annotations. They are building blocks for creating generic
``P.args`` and ``P.kwargs`` are instances respectively of
:class:`ParamSpecArgs` and :class:`ParamSpecKwargs`.
.. attribute:: __name__
The name of the parameter specification.
Parameter specification variables created with ``covariant=True`` or
``contravariant=True`` can be used to declare covariant or contravariant
generic types. The ``bound`` argument is also accepted, similar to
@ -1672,6 +1710,8 @@ These are not used in annotations. They are building blocks for declaring types.
Protocol classes can be generic, for example::
T = TypeVar("T")
class GenProto(Protocol[T]):
def meth(self) -> T:
...
@ -2152,8 +2192,8 @@ Corresponding to collections in :mod:`collections.abc`
A generic version of :class:`collections.abc.Mapping`.
This type can be used as follows::
def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
return word_list[word]
def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
return word_list[word]
.. deprecated:: 3.9
:class:`collections.abc.Mapping` now supports subscripting (``[]``).