Skip to content

Commit c048016

Browse files
AlexWaygoodJelleZijlstra
authored andcommitted
[3.9] [3.10] bpo-46769: Improve documentation for typing.TypeVar (pythonGH-31712) (pythonGH-31941)
* [3.10] bpo-46769: Improve documentation for `typing.TypeVar` (pythonGH-31712) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> (cherry picked from commit 81b425d) * Remove references to `reveal_type`, add new section on `self` types. (cherry picked from commit d5ed8a8) Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
1 parent 3c60190 commit c048016

File tree

2 files changed

+93
-21
lines changed

2 files changed

+93
-21
lines changed

Doc/library/typing.rst

Lines changed: 88 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ subscription to denote expected types for container elements.
214214
def notify_by_email(employees: Sequence[Employee],
215215
overrides: Mapping[str, str]) -> None: ...
216216

217-
Generics can be parameterized by using a new factory available in typing
217+
Generics can be parameterized by using a factory available in typing
218218
called :class:`TypeVar`.
219219

220220
::
@@ -271,16 +271,16 @@ that ``LoggedVar[t]`` is valid as a type::
271271
for var in vars:
272272
var.set(0)
273273

274-
A generic type can have any number of type variables, and type variables may
275-
be constrained::
274+
A generic type can have any number of type variables. All varieties of
275+
:class:`TypeVar` are permissible as parameters for a generic type::
276276

277-
from typing import TypeVar, Generic
278-
...
277+
from typing import TypeVar, Generic, Sequence
279278

280-
T = TypeVar('T')
279+
T = TypeVar('T', contravariant=True)
280+
B = TypeVar('B', bound=Sequence[bytes], covariant=True)
281281
S = TypeVar('S', int, str)
282282

283-
class StrangePair(Generic[T, S]):
283+
class WeirdTrio(Generic[T, B, S]):
284284
...
285285

286286
Each type variable argument to :class:`Generic` must be distinct.
@@ -856,7 +856,8 @@ These are not used in annotations. They are building blocks for creating generic
856856
Usage::
857857

858858
T = TypeVar('T') # Can be anything
859-
A = TypeVar('A', str, bytes) # Must be str or bytes
859+
S = TypeVar('S', bound=str) # Can be any subtype of str
860+
A = TypeVar('A', str, bytes) # Must be exactly str or bytes
860861

861862
Type variables exist primarily for the benefit of static type
862863
checkers. They serve as the parameters for generic types as well
@@ -867,29 +868,95 @@ These are not used in annotations. They are building blocks for creating generic
867868
"""Return a list containing n references to x."""
868869
return [x]*n
869870

870-
def longest(x: A, y: A) -> A:
871-
"""Return the longest of two strings."""
872-
return x if len(x) >= len(y) else y
873871

874-
The latter example's signature is essentially the overloading
875-
of ``(str, str) -> str`` and ``(bytes, bytes) -> bytes``. Also note
876-
that if the arguments are instances of some subclass of :class:`str`,
877-
the return type is still plain :class:`str`.
872+
def print_capitalized(x: S) -> S:
873+
"""Print x capitalized, and return x."""
874+
print(x.capitalize())
875+
return x
876+
877+
878+
def concatenate(x: A, y: A) -> A:
879+
"""Add two strings or bytes objects together."""
880+
return x + y
881+
882+
Note that type variables can be *bound*, *constrained*, or neither, but
883+
cannot be both bound *and* constrained.
884+
885+
Constrained type variables and bound type variables have different
886+
semantics in several important ways. Using a *constrained* type variable
887+
means that the ``TypeVar`` can only ever be solved as being exactly one of
888+
the constraints given::
889+
890+
a = concatenate('one', 'two') # Ok, variable 'a' has type 'str'
891+
b = concatenate(StringSubclass('one'), StringSubclass('two')) # Inferred type of variable 'b' is 'str',
892+
# despite 'StringSubclass' being passed in
893+
c = concatenate('one', b'two') # error: type variable 'A' can be either 'str' or 'bytes' in a function call, but not both
894+
895+
Using a *bound* type variable, however, means that the ``TypeVar`` will be
896+
solved using the most specific type possible::
897+
898+
print_capitalized('a string') # Ok, output has type 'str'
899+
900+
class StringSubclass(str):
901+
pass
902+
903+
print_capitalized(StringSubclass('another string')) # Ok, output has type 'StringSubclass'
904+
print_capitalized(45) # error: int is not a subtype of str
905+
906+
Type variables can be bound to concrete types, abstract types (ABCs or
907+
protocols), and even unions of types::
908+
909+
U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes
910+
V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method
911+
912+
Bound type variables are particularly useful for annotating
913+
:func:`classmethods <classmethod>` that serve as alternative constructors.
914+
In the following example (©
915+
`Raymond Hettinger <https://www.youtube.com/watch?v=HTLu2DFOdTg>`_), the
916+
type variable ``C`` is bound to the ``Circle`` class through the use of a
917+
forward reference. Using this type variable to annotate the
918+
``with_circumference`` classmethod, rather than hardcoding the return type
919+
as ``Circle``, means that a type checker can correctly infer the return
920+
type even if the method is called on a subclass::
921+
922+
import math
923+
924+
C = TypeVar('C', bound='Circle')
925+
926+
class Circle:
927+
"""An abstract circle"""
928+
929+
def __init__(self, radius: float) -> None:
930+
self.radius = radius
931+
932+
# Use a type variable to show that the return type
933+
# will always be an instance of whatever `cls` is
934+
@classmethod
935+
def with_circumference(cls: type[C], circumference: float) -> C:
936+
"""Create a circle with the specified circumference"""
937+
radius = circumference / (math.pi * 2)
938+
return cls(radius)
939+
940+
941+
class Tire(Circle):
942+
"""A specialised circle (made out of rubber)"""
943+
944+
MATERIAL = 'rubber'
945+
946+
947+
c = Circle.with_circumference(3) # Ok, variable 'c' has type 'Circle'
948+
t = Tire.with_circumference(4) # Ok, variable 't' has type 'Tire' (not 'Circle')
878949

879950
At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`. In general,
880951
:func:`isinstance` and :func:`issubclass` should not be used with types.
881952

882953
Type variables may be marked covariant or contravariant by passing
883954
``covariant=True`` or ``contravariant=True``. See :pep:`484` for more
884-
details. By default type variables are invariant. Alternatively,
885-
a type variable may specify an upper bound using ``bound=<type>``.
886-
This means that an actual type substituted (explicitly or implicitly)
887-
for the type variable must be a subclass of the boundary type,
888-
see :pep:`484`.
955+
details. By default, type variables are invariant.
889956

890957
.. data:: AnyStr
891958

892-
``AnyStr`` is a type variable defined as
959+
``AnyStr`` is a :class:`constrained type variable <TypeVar>` defined as
893960
``AnyStr = TypeVar('AnyStr', str, bytes)``.
894961

895962
It is meant to be used for functions that may accept any kind of string

Doc/tools/susp-ignored.csv

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,8 @@ whatsnew/changelog,,::,default::DeprecationWarning
370370
library/importlib.metadata,,:main,"EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')"
371371
library/importlib.metadata,,`,loading the metadata for packages for the indicated ``context``.
372372
library/re,,`,"`"
373+
library/typing,,`,# Type of ``val`` is narrowed to ``str``
374+
library/typing,,`,"# Else, type of ``val`` is narrowed to ``float``."
375+
library/typing,,`,# Type of ``val`` is narrowed to ``List[str]``.
376+
library/typing,,`,# Type of ``val`` remains as ``List[object]``.
377+
library/typing,,`, # will always be an instance of whatever `cls` is

0 commit comments

Comments
 (0)