Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 38 additions & 41 deletions docs/reference/generics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ Here is a very simple generic class that represents a stack:

.. code-block:: python

from typing import TypeVar, Generic

T = TypeVar('T')

class Stack(Generic[T]):
class Stack[T]:
def __init__(self) -> None:
# Create an empty list with items of type T
self.items: list[T] = []
Expand All @@ -37,6 +33,23 @@ Here is a very simple generic class that represents a stack:
def empty(self) -> bool:
return not self.items

.. note::

The type parameter syntax (e.g., ``class Foo[T]:``) is available in Python 3.12 and newer.
For earlier Python versions, generic classes need to be defined using
``TypeVar`` and ``Generic``, as shown below.

For compatibility with older Python versions, the same class may be written as:

.. code-block:: python

from typing import TypeVar, Generic

T = TypeVar('T')

class Stack(Generic[T]):
...

The ``Stack`` class can be used to represent a stack of any type:
``Stack[int]``, ``Stack[tuple[int, str]]``, etc.

Expand All @@ -56,7 +69,7 @@ construction of the instance will be type checked correspondingly.

.. code-block:: python

class Box(Generic[T]):
class Box[T]:
def __init__(self, content: T) -> None:
self.content = content

Expand All @@ -70,17 +83,14 @@ Defining subclasses of generic classes
**************************************

User-defined generic classes and generic classes defined in :py:mod:`typing`
can be used as a base class for another class (generic or non-generic). For example:
can be used as base classes for other classes (generic or non-generic). For example:

.. code-block:: python

from typing import Generic, TypeVar, Mapping, Iterator

KT = TypeVar('KT')
VT = TypeVar('VT')
from typing import Mapping, Iterator

# This is a generic subclass of Mapping
class MyMap(Mapping[KT, VT]):
class MyMap[KT, VT](Mapping[KT, VT]):
def __getitem__(self, k: KT) -> VT: ...
def __iter__(self) -> Iterator[KT]: ...
def __len__(self) -> int: ...
Expand All @@ -96,12 +106,12 @@ can be used as a base class for another class (generic or non-generic). For exam
data: StrDict[int, int] # error: "StrDict" expects no type arguments, but 2 given
data2: StrDict # OK

# This is a user-defined generic class
class Receiver(Generic[T]):
def accept(self, value: T) -> None: ...
# This is a user-defined generic class
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still uses the old syntax. Please make sure all examples (other than the one mentioning the old syntax explicitly) use the new syntax.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @JelleZijlstra, I’ve updated the remaining example to use PEP 695 syntax so that all examples are now consistent, with the old syntax kept only in the explicitly marked compatibility section.

class Receiver[T]:
def accept(self, value: T) -> None: ...

# This is a generic subclass of Receiver
class AdvancedReceiver(Receiver[T]): ...
# This is a generic subclass of Receiver
class AdvancedReceiver(Receiver[T]): ...

.. note::

Expand All @@ -111,33 +121,16 @@ can be used as a base class for another class (generic or non-generic). For exam
protocols like :py:class:`~typing.Iterable`, which use
:ref:`structural subtyping <protocol-types>`.

:py:class:`Generic <typing.Generic>` can be omitted from bases if there are
other base classes that include type variables, such as ``Mapping[KT, VT]``
in the above example. If you include ``Generic[...]`` in bases, then
it should list all type variables present in other bases (or more,
if needed). The order of type variables is defined by the following
rules:

* If ``Generic[...]`` is present, then the order of variables is
always determined by their order in ``Generic[...]``.
* If there are no ``Generic[...]`` in bases, then all type variables
are collected in the lexicographic order (i.e. by first appearance).

For example:

.. code-block:: python

from typing import Generic, TypeVar, Any
from typing import Any
class One[T]: ...
class Another[T]: ...

T = TypeVar('T')
S = TypeVar('S')
U = TypeVar('U')

class One(Generic[T]): ...
class Another(Generic[T]): ...

class First(One[T], Another[S]): ...
class Second(One[T], Another[S], Generic[S, U, T]): ...
class First[T, S](One[T], Another[S]): ...
class Second[S, U, T](One[T], Another[S]): ...

x: First[int, str] # Here T is bound to int, S is bound to str
y: Second[int, str, Any] # Here T is Any, S is int, and U is str
Expand Down Expand Up @@ -205,8 +198,12 @@ the class definition.

.. code-block:: python

# T is the type variable bound by this class
class PairedBox(Generic[T]):
from typing import TypeVar

S = TypeVar('S')

# T is the type parameter bound by this class
class PairedBox[T]:
def __init__(self, content: T) -> None:
self.content = content

Expand Down