bpo-46769: Improve documentation for typing.TypeVar (GH-31712)

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
Alex Waygood 2022-03-16 15:51:26 +00:00 committed by GitHub
parent d56a237e16
commit 81b425d4dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -249,7 +249,7 @@ subscription to denote expected types for container elements.
def notify_by_email(employees: Sequence[Employee], def notify_by_email(employees: Sequence[Employee],
overrides: Mapping[str, str]) -> None: ... overrides: Mapping[str, str]) -> None: ...
Generics can be parameterized by using a new factory available in typing Generics can be parameterized by using a factory available in typing
called :class:`TypeVar`. called :class:`TypeVar`.
:: ::
@ -306,16 +306,16 @@ that ``LoggedVar[t]`` is valid as a type::
for var in vars: for var in vars:
var.set(0) var.set(0)
A generic type can have any number of type variables, and type variables may A generic type can have any number of type variables. All varieties of
be constrained:: :class:`TypeVar` are permissible as parameters for a generic type::
from typing import TypeVar, Generic from typing import TypeVar, Generic, Sequence
...
T = TypeVar('T') T = TypeVar('T', contravariant=True)
B = TypeVar('B', bound=Sequence[bytes], covariant=True)
S = TypeVar('S', int, str) S = TypeVar('S', int, str)
class StrangePair(Generic[T, S]): class WeirdTrio(Generic[T, B, S]):
... ...
Each type variable argument to :class:`Generic` must be distinct. Each type variable argument to :class:`Generic` must be distinct.
@ -1165,7 +1165,8 @@ These are not used in annotations. They are building blocks for creating generic
Usage:: Usage::
T = TypeVar('T') # Can be anything T = TypeVar('T') # Can be anything
A = TypeVar('A', str, bytes) # Must be str or bytes 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 Type variables exist primarily for the benefit of static type
checkers. They serve as the parameters for generic types as well checkers. They serve as the parameters for generic types as well
@ -1176,25 +1177,58 @@ These are not used in annotations. They are building blocks for creating generic
"""Return a list containing n references to x.""" """Return a list containing n references to x."""
return [x]*n return [x]*n
def longest(x: A, y: A) -> A:
"""Return the longest of two strings."""
return x if len(x) >= len(y) else y
The latter example's signature is essentially the overloading def print_capitalized(x: S) -> S:
of ``(str, str) -> str`` and ``(bytes, bytes) -> bytes``. Also note """Print x capitalized, and return x."""
that if the arguments are instances of some subclass of :class:`str`, print(x.capitalize())
the return type is still plain :class:`str`. return x
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.
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::
x = print_capitalized('a string')
reveal_type(x) # revealed type is str
class StringSubclass(str):
pass
y = print_capitalized(StringSubclass('another string'))
reveal_type(y) # revealed type is StringSubclass
z = print_capitalized(45) # error: int is not a subtype of str
Type variables can be bound to concrete types, abstract types (ABCs or
protocols), and even unions of types::
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::
a = concatenate('one', 'two')
reveal_type(a) # revealed type is str
b = concatenate(StringSubclass('one'), StringSubclass('two'))
reveal_type(b) # revealed type is str, despite StringSubclass being passed in
c = concatenate('one', b'two') # error: type variable 'A' can be either str or bytes in a function call, but not both
At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`. In general, At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`. In general,
:func:`isinstance` and :func:`issubclass` should not be used with types. :func:`isinstance` and :func:`issubclass` should not be used with types.
Type variables may be marked covariant or contravariant by passing Type variables may be marked covariant or contravariant by passing
``covariant=True`` or ``contravariant=True``. See :pep:`484` for more ``covariant=True`` or ``contravariant=True``. See :pep:`484` for more
details. By default type variables are invariant. Alternatively, details. By default, type variables are invariant.
a type variable may specify an upper bound using ``bound=<type>``.
This means that an actual type substituted (explicitly or implicitly)
for the type variable must be a subclass of the boundary type,
see :pep:`484`.
.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False) .. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)
@ -1296,7 +1330,7 @@ These are not used in annotations. They are building blocks for creating generic
.. data:: AnyStr .. data:: AnyStr
``AnyStr`` is a type variable defined as ``AnyStr`` is a :class:`constrained type variable <TypeVar>` defined as
``AnyStr = TypeVar('AnyStr', str, bytes)``. ``AnyStr = TypeVar('AnyStr', str, bytes)``.
It is meant to be used for functions that may accept any kind of string It is meant to be used for functions that may accept any kind of string