Skip to content
1 change: 1 addition & 0 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ called :class:`TypeVar`.
def first(l: Sequence[T]) -> T: # Generic function
return l[0]

.. _user-defined-generics:

User-defined generic types
==========================
Expand Down
91 changes: 75 additions & 16 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2215,22 +2215,73 @@ 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 :term:`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

:ref:`Generics`, :ref:`user-defined generics<user-defined-generics>` and :class:`typing.Generic`
Documentation on how 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.

.. seealso::
.. note::
Comment thread
AlexWaygood marked this conversation as resolved.
Outdated
``__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.
Comment thread
AlexWaygood marked this conversation as resolved.
Outdated

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.
Comment thread
AlexWaygood marked this conversation as resolved.
Outdated

:pep:`560` - Core support for typing module and generic types

*__class_getitem__* versus *__getitem__*
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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. 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.
Comment thread
AlexWaygood marked this conversation as resolved.
Outdated

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.
Comment thread
AlexWaygood marked this conversation as resolved.
Outdated


.. _callable-types:
Expand Down Expand Up @@ -2330,20 +2381,28 @@ through the object's keys; for sequences, it should iterate through the values.

.. method:: object.__getitem__(self, key)

Called to implement evaluation of ``self[key]``. For sequence types, the
accepted keys should be integers and slice objects. Note that the special
interpretation of negative indexes (if the class wishes to emulate a sequence
type) is up to the :meth:`__getitem__` method. If *key* is of an inappropriate
type, :exc:`TypeError` may be raised; if of a value outside the set of indexes
for the sequence (after any special interpretation of negative values),
:exc:`IndexError` should be raised. For mapping types, if *key* is missing (not
in the container), :exc:`KeyError` should be raised.
Called to implement evaluation of ``self[key]``, which is translated by the
interpreter to ``type(self).__getitem__(self, key)``. For :term:`sequence`
types, the accepted keys should be integers and slice objects. Note that
the special interpretation of negative indexes (if the class wishes to
emulate a :term:`sequence` type) is up to the :meth:`__getitem__` method. If
*key* is of an inappropriate type, :exc:`TypeError` may be raised; if of a
value outside the set of indexes for the sequence (after any special
interpretation of negative values), :exc:`IndexError` should be raised. For
:term:`mapping` types, if *key* is missing (not in the container),
:exc:`KeyError` should be raised.

.. note::

: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__()``.
Comment thread
AlexWaygood marked this conversation as resolved.
Outdated


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

Expand Down