Skip to content

Commit e7f1f22

Browse files
AlexWaygoodmiss-islington
authored andcommitted
[doc] bpo-45680: Disambiguate __getitem__ and __class_getitem__ in the data model (pythonGH-29389)
The documentation explaining Python's data model does not adequately explain the differences between ``__getitem__`` and ``__class_getitem__``, nor does it explain when each is called. There is an attempt at explaining ``__class_getitem__`` in the documentation for ``GenericAlias`` objects, but this does not give sufficient clarity into how the method works. Moreover, it is the wrong place for that information to be found; the explanation of ``__class_getitem__`` should be in the documentation explaining the data model. This PR has been split off from pythonGH-29335. (cherry picked from commit 31b3a70) Co-authored-by: Alex Waygood <[email protected]>
1 parent feccea6 commit e7f1f22

File tree

2 files changed

+147
-18
lines changed

2 files changed

+147
-18
lines changed

Doc/library/typing.rst

+1
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ called :class:`TypeVar`.
223223
def first(l: Sequence[T]) -> T: # Generic function
224224
return l[0]
225225

226+
.. _user-defined-generics:
226227

227228
User-defined generic types
228229
==========================

Doc/reference/datamodel.rst

+146-18
Original file line numberDiff line numberDiff line change
@@ -2144,22 +2144,142 @@ case the instance is itself a class.
21442144
Emulating generic types
21452145
-----------------------
21462146

2147-
One can implement the generic class syntax as specified by :pep:`484`
2148-
(for example ``List[int]``) by defining a special method:
2147+
When using :term:`type annotations<annotation>`, it is often useful to
2148+
*parameterize* a :term:`generic type` using Python's square-brackets notation.
2149+
For example, the annotation ``list[int]`` might be used to signify a
2150+
:class:`list` in which all the elements are of type :class:`int`.
2151+
2152+
.. seealso::
2153+
2154+
:pep:`484` - Type Hints
2155+
Introducing Python's framework for type annotations
2156+
2157+
:ref:`Generic Alias Types<types-genericalias>`
2158+
Documentation for objects representing parameterized generic classes
2159+
2160+
:ref:`Generics`, :ref:`user-defined generics<user-defined-generics>` and :class:`typing.Generic`
2161+
Documentation on how to implement generic classes that can be
2162+
parameterized at runtime and understood by static type-checkers.
2163+
2164+
A class can *generally* only be parameterized if it defines the special
2165+
class method ``__class_getitem__()``.
21492166

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

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

2155-
This method is looked up on the class object itself, and when defined in
2156-
the class body, this method is implicitly a class method. Note, this
2157-
mechanism is primarily reserved for use with static type hints, other usage
2158-
is discouraged.
2172+
When defined on a class, ``__class_getitem__()`` is automatically a class
2173+
method. As such, there is no need for it to be decorated with
2174+
:func:`@classmethod<classmethod>` when it is defined.
21592175

2160-
.. seealso::
21612176

2162-
:pep:`560` - Core support for typing module and generic types
2177+
The purpose of *__class_getitem__*
2178+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2179+
2180+
The purpose of :meth:`~object.__class_getitem__` is to allow runtime
2181+
parameterization of standard-library generic classes in order to more easily
2182+
apply :term:`type hints<type hint>` to these classes.
2183+
2184+
To implement custom generic classes that can be parameterized at runtime and
2185+
understood by static type-checkers, users should either inherit from a standard
2186+
library class that already implements :meth:`~object.__class_getitem__`, or
2187+
inherit from :class:`typing.Generic`, which has its own implementation of
2188+
``__class_getitem__()``.
2189+
2190+
Custom implementations of :meth:`~object.__class_getitem__` on classes defined
2191+
outside of the standard library may not be understood by third-party
2192+
type-checkers such as mypy. Using ``__class_getitem__()`` on any class for
2193+
purposes other than type hinting is discouraged.
2194+
2195+
2196+
.. _classgetitem-versus-getitem:
2197+
2198+
2199+
*__class_getitem__* versus *__getitem__*
2200+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2201+
2202+
Usually, the :ref:`subscription<subscriptions>` of an object using square
2203+
brackets will call the :meth:`~object.__getitem__` instance method defined on
2204+
the object's class. However, if the object being subscribed is itself a class,
2205+
the class method :meth:`~object.__class_getitem__` may be called instead.
2206+
``__class_getitem__()`` should return a :ref:`GenericAlias<types-genericalias>`
2207+
object if it is properly defined.
2208+
2209+
Presented with the :term:`expression` ``obj[x]``, the Python interpreter
2210+
follows something like the following process to decide whether
2211+
:meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` should be
2212+
called::
2213+
2214+
from inspect import isclass
2215+
2216+
def subscribe(obj, x):
2217+
"""Return the result of the expression `obj[x]`"""
2218+
2219+
class_of_obj = type(obj)
2220+
2221+
# If the class of obj defines __getitem__,
2222+
# call class_of_obj.__getitem__(obj, x)
2223+
if hasattr(class_of_obj, '__getitem__'):
2224+
return class_of_obj.__getitem__(obj, x)
2225+
2226+
# Else, if obj is a class and defines __class_getitem__,
2227+
# call obj.__class_getitem__(x)
2228+
elif isclass(obj) and hasattr(obj, '__class_getitem__'):
2229+
return obj.__class_getitem__(x)
2230+
2231+
# Else, raise an exception
2232+
else:
2233+
raise TypeError(
2234+
f"'{class_of_obj.__name__}' object is not subscriptable"
2235+
)
2236+
2237+
In Python, all classes are themselves instances of other classes. The class of
2238+
a class is known as that class's :term:`metaclass`, and most classes have the
2239+
:class:`type` class as their metaclass. :class:`type` does not define
2240+
:meth:`~object.__getitem__`, meaning that expressions such as ``list[int]``,
2241+
``dict[str, float]`` and ``tuple[str, bytes]`` all result in
2242+
:meth:`~object.__class_getitem__` being called::
2243+
2244+
>>> # list has class "type" as its metaclass, like most classes:
2245+
>>> type(list)
2246+
<class 'type'>
2247+
>>> type(dict) == type(list) == type(tuple) == type(str) == type(bytes)
2248+
True
2249+
>>> # "list[int]" calls "list.__class_getitem__(int)"
2250+
>>> list[int]
2251+
list[int]
2252+
>>> # list.__class_getitem__ returns a GenericAlias object:
2253+
>>> type(list[int])
2254+
<class 'types.GenericAlias'>
2255+
2256+
However, if a class has a custom metaclass that defines
2257+
:meth:`~object.__getitem__`, subscribing the class may result in different
2258+
behaviour. An example of this can be found in the :mod:`enum` module::
2259+
2260+
>>> from enum import Enum
2261+
>>> class Menu(Enum):
2262+
... """A breakfast menu"""
2263+
... SPAM = 'spam'
2264+
... BACON = 'bacon'
2265+
...
2266+
>>> # Enum classes have a custom metaclass:
2267+
>>> type(Menu)
2268+
<class 'enum.EnumMeta'>
2269+
>>> # EnumMeta defines __getitem__,
2270+
>>> # so __class_getitem__ is not called,
2271+
>>> # and the result is not a GenericAlias object:
2272+
>>> Menu['SPAM']
2273+
<Menu.SPAM: 'spam'>
2274+
>>> type(Menu['SPAM'])
2275+
<enum 'Menu'>
2276+
2277+
2278+
.. seealso::
2279+
:pep:`560` - Core Support for typing module and generic types
2280+
Introducing :meth:`~object.__class_getitem__`, and outlining when a
2281+
:ref:`subscription<subscriptions>` results in ``__class_getitem__()``
2282+
being called instead of :meth:`~object.__getitem__`
21632283

21642284

21652285
.. _callable-types:
@@ -2259,19 +2379,27 @@ through the object's keys; for sequences, it should iterate through the values.
22592379

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

2262-
Called to implement evaluation of ``self[key]``. For sequence types, the
2263-
accepted keys should be integers and slice objects. Note that the special
2264-
interpretation of negative indexes (if the class wishes to emulate a sequence
2265-
type) is up to the :meth:`__getitem__` method. If *key* is of an inappropriate
2266-
type, :exc:`TypeError` may be raised; if of a value outside the set of indexes
2267-
for the sequence (after any special interpretation of negative values),
2268-
:exc:`IndexError` should be raised. For mapping types, if *key* is missing (not
2269-
in the container), :exc:`KeyError` should be raised.
2382+
Called to implement evaluation of ``self[key]``. For :term:`sequence` types,
2383+
the accepted keys should be integers and slice objects. Note that the
2384+
special interpretation of negative indexes (if the class wishes to emulate a
2385+
:term:`sequence` type) is up to the :meth:`__getitem__` method. If *key* is
2386+
of an inappropriate type, :exc:`TypeError` may be raised; if of a value
2387+
outside the set of indexes for the sequence (after any special
2388+
interpretation of negative values), :exc:`IndexError` should be raised. For
2389+
:term:`mapping` types, if *key* is missing (not in the container),
2390+
:exc:`KeyError` should be raised.
2391+
2392+
.. note::
2393+
2394+
:keyword:`for` loops expect that an :exc:`IndexError` will be raised for
2395+
illegal indexes to allow proper detection of the end of the sequence.
22702396

22712397
.. note::
22722398

2273-
:keyword:`for` loops expect that an :exc:`IndexError` will be raised for illegal
2274-
indexes to allow proper detection of the end of the sequence.
2399+
When :ref:`subscripting<subscriptions>` a *class*, the special
2400+
class method :meth:`~object.__class_getitem__` may be called instead of
2401+
``__getitem__()``. See :ref:`classgetitem-versus-getitem` for more
2402+
details.
22752403

22762404

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

0 commit comments

Comments
 (0)