Skip to content
79 changes: 71 additions & 8 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2215,23 +2215,80 @@ case the instance is itself a class.
Emulating generic types
-----------------------

One can implement the generic class syntax as specified by :pep:`484`
(for example ``List[int]``) by defining a special method:
When using :term:`type annotations<annotation>`, it is often useful to
*parameterize* a generic type using Python's square-brackets notation.
For example, the annotation ``list[int]`` might be used to signify a
:class:`list` in which all the elements are of type :class:`int`.

.. seealso::

:pep:`484` - Type Hints
Introducing Python's framework for type annotations

:ref:`Generic Alias Types<types-genericalias>`
Documentation for objects representing parameterized generic classes

:class:`typing.Generic`
Inherit from :class:`typing.Generic` to implement generic classes that
can be parameterized at runtime and understood by static type-checkers.

A class can generally only be parameterized if it defines the special
classmethod ``__class_getitem__()``.

.. classmethod:: object.__class_getitem__(cls, key)

Return an object representing the specialization of a generic class
by type arguments found in *key*.

This method is looked up on the class object itself, and when defined in
the class body, this method is implicitly a class method. Note, this
mechanism is primarily reserved for use with static type hints, other usage
is discouraged.
``__getitem__`` *versus* ``__class_getitem__``

.. seealso::
Usually, the :ref:`subscription <subscriptions>` of an object in Python
using the square-brackets notation will call the :meth:`~object.__getitem__`
instance method defined on the object's class.

:pep:`560` - Core support for typing module and generic types
For example, if we have a list ``food`` as follows::

food = ['spam', 'eggs', 'bacon']

Calling ``food[0]`` will return the same value as calling::

type(food).__getitem__(food, 0)

However, if a class defines the classmethod ``__class_getitem__()``, then
the subscription of that class may call the class's implementation of
``__class_getitem__()`` rather than :meth:`~object.__getitem__`.
``__class_getitem__()`` should return a
:ref:`GenericAlias<types-genericalias>` object if it is properly defined.

For example, because the :class:`list` class defines
``__class_getitem__()``, calling ``list[str]`` is equivalent to calling::

list.__class_getitem__(str)

rather than::

type(list).__getitem__(list, str)

.. note::
If :meth:`~object.__getitem__` is defined by a class's :term:`metaclass`, it
will take precedence over a ``__class_getitem__()`` classmethod
defined by the class. See :pep:`560` for more details.

.. note::
``__class_getitem__()`` was introduced to implement runtime parameterization
of standard-library generic classes in order to more easily apply
:term:`type-hints<type hint>` to these classes.

To implement custom generic classes that can be parameterized at runtime and
understood by static type-checkers, users should either inherit from a
standard library class that already implements ``__class_getitem__()``, or
inherit from :class:`typing.Generic`, which has its own implementation of
``__class_getitem__()``.

Custom implementations of ``__class_getitem__()`` on classes defined outside
of the standard library may not be understood by third-party type-checkers
such as mypy. Using ``__class_getitem__()`` on any class for purposes other
than type-hinting is discouraged.

.. _callable-types:

Expand Down Expand Up @@ -2344,6 +2401,12 @@ through the object's keys; for sequences, it should iterate through the values.
:keyword:`for` loops expect that an :exc:`IndexError` will be raised for illegal
indexes to allow proper detection of the end of the sequence.

.. note::

When :ref:`subscripting<subscriptions>` a *class*, the special
classmethod :meth:`~object.__class_getitem__` may be called instead of
``__getitem__()``.


.. method:: object.__setitem__(self, key, value)

Expand Down