Skip to content

[doc] bpo-45680: Disambiguate __getitem__ and __class_getitem__ in the data model. #29389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 18, 2021
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::
``__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.

: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.

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.


.. _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__()``.


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

Expand Down