mirror of
https://github.com/python/cpython.git
synced 2025-09-30 04:15:43 +00:00
bpo-45024 and bpo-23864: Document how interface testing works with the collections ABCs (GH-28218)
This commit is contained in:
parent
794430700d
commit
62fa613f6a
2 changed files with 159 additions and 54 deletions
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
.. testsetup:: *
|
.. testsetup:: *
|
||||||
|
|
||||||
from collections import *
|
from collections.abc import *
|
||||||
import itertools
|
import itertools
|
||||||
__name__ = '<doctest>'
|
__name__ = '<doctest>'
|
||||||
|
|
||||||
|
@ -24,6 +24,86 @@ This module provides :term:`abstract base classes <abstract base class>` that
|
||||||
can be used to test whether a class provides a particular interface; for
|
can be used to test whether a class provides a particular interface; for
|
||||||
example, whether it is hashable or whether it is a mapping.
|
example, whether it is hashable or whether it is a mapping.
|
||||||
|
|
||||||
|
An :func:`issubclass` or :func:`isinstance` test for an interface works in one
|
||||||
|
of three ways.
|
||||||
|
|
||||||
|
1) A newly written class can inherit directly from one of the
|
||||||
|
abstract base classes. The class must supply the required abstract
|
||||||
|
methods. The remaining mixin methods come from inheritance and can be
|
||||||
|
overridden if desired. Other methods may be added as needed:
|
||||||
|
|
||||||
|
.. testcode::
|
||||||
|
|
||||||
|
class C(Sequence): # Direct inheritance
|
||||||
|
def __init__(self): ... # Extra method not required by the ABC
|
||||||
|
def __getitem__(self, index): ... # Required abstract method
|
||||||
|
def __len__(self): ... # Required abstract method
|
||||||
|
def count(self, value): ... # Optionally override a mixin method
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> issubclass(C, Sequence)
|
||||||
|
True
|
||||||
|
>>> isinstance(C(), Sequence)
|
||||||
|
True
|
||||||
|
|
||||||
|
2) Existing classes and built-in classes can be registered as "virtual
|
||||||
|
subclasses" of the ABCs. Those classes should define the full API
|
||||||
|
including all of the abstract methods and all of the mixin methods.
|
||||||
|
This lets users rely on :func:`issubclass` or :func:`isinstance` tests
|
||||||
|
to determine whether the full interface is supported. The exception to
|
||||||
|
this rule is for methods that are automatically inferred from the rest
|
||||||
|
of the API:
|
||||||
|
|
||||||
|
.. testcode::
|
||||||
|
|
||||||
|
class D: # No inheritance
|
||||||
|
def __init__(self): ... # Extra method not required by the ABC
|
||||||
|
def __getitem__(self, index): ... # Abstract method
|
||||||
|
def __len__(self): ... # Abstract method
|
||||||
|
def count(self, value): ... # Mixin method
|
||||||
|
def index(self, value): ... # Mixin method
|
||||||
|
|
||||||
|
Sequence.register(D) # Register instead of inherit
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> issubclass(D, Sequence)
|
||||||
|
True
|
||||||
|
>>> isinstance(D(), Sequence)
|
||||||
|
True
|
||||||
|
|
||||||
|
In this example, class :class:`D` does not need to define
|
||||||
|
``__contains__``, ``__iter__``, and ``__reversed__`` because the
|
||||||
|
:ref:`in-operator <comparisons>`, the :term:`iteration <iterable>`
|
||||||
|
logic, and the :func:`reversed` function automatically fall back to
|
||||||
|
using ``__getitem__`` and ``__len__``.
|
||||||
|
|
||||||
|
3) Some simple interfaces are directly recognizable by the presence of
|
||||||
|
the required methods (unless those methods have been set to
|
||||||
|
:const:`None`):
|
||||||
|
|
||||||
|
.. testcode::
|
||||||
|
|
||||||
|
class E:
|
||||||
|
def __iter__(self): ...
|
||||||
|
def __next__(next): ...
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> issubclass(E, Iterable)
|
||||||
|
True
|
||||||
|
>>> isinstance(E(), Iterable)
|
||||||
|
True
|
||||||
|
|
||||||
|
Complex interfaces do not support this last technique because an
|
||||||
|
interface is more than just the presence of method names. Interfaces
|
||||||
|
specify semantics and relationships between methods that cannot be
|
||||||
|
inferred solely from the presence of specific method names. For
|
||||||
|
example, knowing that a class supplies ``__getitem__``, ``__len__``, and
|
||||||
|
``__iter__`` is insufficient for distinguishing a :class:`Sequence` from
|
||||||
|
a :class:`Mapping`.
|
||||||
|
|
||||||
|
|
||||||
.. _collections-abstract-base-classes:
|
.. _collections-abstract-base-classes:
|
||||||
|
|
||||||
|
@ -34,18 +114,18 @@ The collections module offers the following :term:`ABCs <abstract base class>`:
|
||||||
|
|
||||||
.. tabularcolumns:: |l|L|L|L|
|
.. tabularcolumns:: |l|L|L|L|
|
||||||
|
|
||||||
========================== ====================== ======================= ====================================================
|
============================== ====================== ======================= ====================================================
|
||||||
ABC Inherits from Abstract Methods Mixin Methods
|
ABC Inherits from Abstract Methods Mixin Methods
|
||||||
========================== ====================== ======================= ====================================================
|
============================== ====================== ======================= ====================================================
|
||||||
:class:`Container` ``__contains__``
|
:class:`Container` [1]_ ``__contains__``
|
||||||
:class:`Hashable` ``__hash__``
|
:class:`Hashable` [1]_ ``__hash__``
|
||||||
:class:`Iterable` ``__iter__``
|
:class:`Iterable` [1]_ [2]_ ``__iter__``
|
||||||
:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__``
|
:class:`Iterator` [1]_ :class:`Iterable` ``__next__`` ``__iter__``
|
||||||
:class:`Reversible` :class:`Iterable` ``__reversed__``
|
:class:`Reversible` [1]_ :class:`Iterable` ``__reversed__``
|
||||||
:class:`Generator` :class:`Iterator` ``send``, ``throw`` ``close``, ``__iter__``, ``__next__``
|
:class:`Generator` [1]_ :class:`Iterator` ``send``, ``throw`` ``close``, ``__iter__``, ``__next__``
|
||||||
:class:`Sized` ``__len__``
|
:class:`Sized` [1]_ ``__len__``
|
||||||
:class:`Callable` ``__call__``
|
:class:`Callable` [1]_ ``__call__``
|
||||||
:class:`Collection` :class:`Sized`, ``__contains__``,
|
:class:`Collection` [1]_ :class:`Sized`, ``__contains__``,
|
||||||
:class:`Iterable`, ``__iter__``,
|
:class:`Iterable`, ``__iter__``,
|
||||||
:class:`Container` ``__len__``
|
:class:`Container` ``__len__``
|
||||||
|
|
||||||
|
@ -89,12 +169,31 @@ ABC Inherits from Abstract Methods Mixin
|
||||||
:class:`Set` ``__iter__``
|
:class:`Set` ``__iter__``
|
||||||
:class:`ValuesView` :class:`MappingView`, ``__contains__``, ``__iter__``
|
:class:`ValuesView` :class:`MappingView`, ``__contains__``, ``__iter__``
|
||||||
:class:`Collection`
|
:class:`Collection`
|
||||||
:class:`Awaitable` ``__await__``
|
:class:`Awaitable` [1]_ ``__await__``
|
||||||
:class:`Coroutine` :class:`Awaitable` ``send``, ``throw`` ``close``
|
:class:`Coroutine` [1]_ :class:`Awaitable` ``send``, ``throw`` ``close``
|
||||||
:class:`AsyncIterable` ``__aiter__``
|
:class:`AsyncIterable` [1]_ ``__aiter__``
|
||||||
:class:`AsyncIterator` :class:`AsyncIterable` ``__anext__`` ``__aiter__``
|
:class:`AsyncIterator` [1]_ :class:`AsyncIterable` ``__anext__`` ``__aiter__``
|
||||||
:class:`AsyncGenerator` :class:`AsyncIterator` ``asend``, ``athrow`` ``aclose``, ``__aiter__``, ``__anext__``
|
:class:`AsyncGenerator` [1]_ :class:`AsyncIterator` ``asend``, ``athrow`` ``aclose``, ``__aiter__``, ``__anext__``
|
||||||
========================== ====================== ======================= ====================================================
|
============================== ====================== ======================= ====================================================
|
||||||
|
|
||||||
|
|
||||||
|
.. rubric:: Footnotes
|
||||||
|
|
||||||
|
.. [1] These ABCs override :meth:`object.__subclasshook__` to support
|
||||||
|
testing an interface by verifying the required methods are present
|
||||||
|
and have not been set to :const:`None`. This only works for simple
|
||||||
|
interfaces. More complex interfaces require registration or direct
|
||||||
|
subclassing.
|
||||||
|
|
||||||
|
.. [2] Checking ``isinstance(obj, Iterable)`` detects classes that are
|
||||||
|
registered as :class:`Iterable` or that have an :meth:`__iter__`
|
||||||
|
method, but it does not detect classes that iterate with the
|
||||||
|
:meth:`__getitem__` method. The only reliable way to determine
|
||||||
|
whether an object is :term:`iterable` is to call ``iter(obj)``.
|
||||||
|
|
||||||
|
|
||||||
|
Collections Abstract Base Classes -- Detailed Descriptions
|
||||||
|
----------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
.. class:: Container
|
.. class:: Container
|
||||||
|
@ -244,8 +343,10 @@ ABC Inherits from Abstract Methods Mixin
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
|
Examples and Recipes
|
||||||
|
--------------------
|
||||||
|
|
||||||
These ABCs allow us to ask classes or instances if they provide
|
ABCs allow us to ask classes or instances if they provide
|
||||||
particular functionality, for example::
|
particular functionality, for example::
|
||||||
|
|
||||||
size = None
|
size = None
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
:mod:`collections.abc` documentation has been expanded to explicitly cover
|
||||||
|
how instance and subclass checks work, with additional doctest examples and
|
||||||
|
an exhaustive list of ABCs which test membership purely by presence of the
|
||||||
|
right :term:`special method`\s. Patch by Raymond Hettinger.
|
Loading…
Add table
Add a link
Reference in a new issue