Skip to content

Commit 6336ce1

Browse files
authored
Discuss upper bounds before self types in documentation (#17827)
The explanation of self types uses upper bounds, so it makes sense to discuss them earlier. I also made some other minor documentation tweaks to improve clarity.
1 parent f6520c8 commit 6336ce1

File tree

1 file changed

+56
-58
lines changed

1 file changed

+56
-58
lines changed

docs/source/generics.rst

Lines changed: 56 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,53 @@ inferred by mypy. This is not valid:
315315
If you really need this, you can define a generic class with a ``__call__``
316316
method.
317317

318+
.. _type-variable-upper-bound:
319+
320+
Type variables with upper bounds
321+
********************************
322+
323+
A type variable can also be restricted to having values that are
324+
subtypes of a specific type. This type is called the upper bound of
325+
the type variable, and it is specified using ``T: <bound>`` when using the
326+
Python 3.12 syntax. In the definition of a generic function or a generic
327+
class that uses such a type variable ``T``, the type represented by ``T``
328+
is assumed to be a subtype of its upper bound, so you can use methods
329+
of the upper bound on values of type ``T`` (Python 3.12 syntax):
330+
331+
.. code-block:: python
332+
333+
from typing import SupportsAbs
334+
335+
def max_by_abs[T: SupportsAbs[float]](*xs: T) -> T:
336+
# We can use abs(), because T is a subtype of SupportsAbs[float].
337+
return max(xs, key=abs)
338+
339+
An upper bound can also be specified with the ``bound=...`` keyword
340+
argument to :py:class:`~typing.TypeVar`.
341+
Here is the example using the legacy syntax (Python 3.11 and earlier):
342+
343+
.. code-block:: python
344+
345+
from typing import TypeVar, SupportsAbs
346+
347+
T = TypeVar('T', bound=SupportsAbs[float])
348+
349+
def max_by_abs(*xs: T) -> T:
350+
return max(xs, key=abs)
351+
352+
In a call to such a function, the type ``T`` must be replaced by a
353+
type that is a subtype of its upper bound. Continuing the example
354+
above:
355+
356+
.. code-block:: python
357+
358+
max_by_abs(-3.5, 2) # Okay, has type 'float'
359+
max_by_abs(5+6j, 7) # Okay, has type 'complex'
360+
max_by_abs('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float]
361+
362+
Type parameters of generic classes may also have upper bounds, which
363+
restrict the valid values for the type parameter in the same way.
364+
318365
.. _generic-methods-and-generic-self:
319366

320367
Generic methods and generic self
@@ -400,8 +447,7 @@ or :py:class:`Type[T] <typing.Type>` (Python 3.12 syntax):
400447
401448
a, b = SuperFriend.make_pair()
402449
403-
Here is the same example using the legacy syntax (3.11 and earlier, though
404-
3.9 and later can use lower-case ``type[T]``):
450+
Here is the same example using the legacy syntax (3.11 and earlier):
405451

406452
.. code-block:: python
407453
@@ -433,7 +479,7 @@ or a deserialization method returns the actual type of self. Therefore
433479
you may need to silence mypy inside these methods (but not at the call site),
434480
possibly by making use of the ``Any`` type or a ``# type: ignore`` comment.
435481

436-
Note that mypy lets you use generic self types in certain unsafe ways
482+
Mypy lets you use generic self types in certain unsafe ways
437483
in order to support common idioms. For example, using a generic
438484
self type in an argument type is accepted even though it's unsafe (Python 3.12
439485
syntax):
@@ -647,59 +693,13 @@ contravariant, use type variables defined with special keyword arguments
647693
my_box = Box(Square())
648694
look_into(my_box) # OK, but mypy would complain here for an invariant type
649695
650-
.. _type-variable-upper-bound:
651-
652-
Type variables with upper bounds
653-
********************************
654-
655-
A type variable can also be restricted to having values that are
656-
subtypes of a specific type. This type is called the upper bound of
657-
the type variable, and it is specified using ``T: <bound>`` when using the
658-
Python 3.12 syntax. In the definition of a generic function that uses
659-
such a type variable ``T``, the type represented by ``T`` is assumed
660-
to be a subtype of its upper bound, so the function can use methods
661-
of the upper bound on values of type ``T`` (Python 3.12 syntax):
662-
663-
.. code-block:: python
664-
665-
from typing import SupportsAbs
666-
667-
def max_by_abs[T: SupportsAbs[float]](*xs: T) -> T:
668-
# Okay, because T is a subtype of SupportsAbs[float].
669-
return max(xs, key=abs)
670-
671-
An upper bound can also be specified with the ``bound=...`` keyword
672-
argument to :py:class:`~typing.TypeVar`.
673-
Here is the example using the legacy syntax (Python 3.11 and earlier):
674-
675-
.. code-block:: python
676-
677-
from typing import TypeVar, SupportsAbs
678-
679-
T = TypeVar('T', bound=SupportsAbs[float])
680-
681-
def max_by_abs(*xs: T) -> T:
682-
return max(xs, key=abs)
683-
684-
In a call to such a function, the type ``T`` must be replaced by a
685-
type that is a subtype of its upper bound. Continuing the example
686-
above:
687-
688-
.. code-block:: python
689-
690-
max_by_abs(-3.5, 2) # Okay, has type float.
691-
max_by_abs(5+6j, 7) # Okay, has type complex.
692-
max_by_abs('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float].
693-
694-
Type parameters of generic classes may also have upper bounds, which
695-
restrict the valid values for the type parameter in the same way.
696-
697696
.. _type-variable-value-restriction:
698697

699698
Type variables with value restriction
700699
*************************************
701700

702-
By default, a type variable can be replaced with any type. However, sometimes
701+
By default, a type variable can be replaced with any type -- or any type that
702+
is a subtype of the upper bound, which defaults to ``object``. However, sometimes
703703
it's useful to have a type variable that can only have some specific types
704704
as its value. A typical example is a type variable that can only have values
705705
``str`` and ``bytes``. This lets us define a function that can concatenate
@@ -758,11 +758,10 @@ You may expect that the type of ``ss`` is ``S``, but the type is
758758
actually ``str``: a subtype gets promoted to one of the valid values
759759
for the type variable, which in this case is ``str``.
760760

761-
This is thus
762-
subtly different from *bounded quantification* in languages such as
763-
Java, where the return type would be ``S``. The way mypy implements
764-
this is correct for ``concat``, since ``concat`` actually returns a
765-
``str`` instance in the above example:
761+
This is thus subtly different from using ``str | bytes`` as an upper bound,
762+
where the return type would be ``S`` (see :ref:`type-variable-upper-bound`).
763+
Using a value restriction is correct for ``concat``, since ``concat``
764+
actually returns a ``str`` instance in the above example:
766765

767766
.. code-block:: python
768767
@@ -776,8 +775,7 @@ value of :py:func:`re.compile`, where ``S`` can be either ``str``
776775
or ``bytes``. Regular expressions can be based on a string or a
777776
bytes pattern.
778777

779-
A type variable may not have both a value restriction and an upper bound
780-
(see :ref:`type-variable-upper-bound`).
778+
A type variable may not have both a value restriction and an upper bound.
781779

782780
Note that you may come across :py:data:`~typing.AnyStr` imported from
783781
:py:mod:`typing`. This feature is now deprecated, but it means the same

0 commit comments

Comments
 (0)