From 5d45b50da03d162eb6378e29ac879f655584a79e Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 12:19:04 -0500 Subject: [PATCH 01/15] Document how interface testing works with the collections ABCs --- Doc/library/collections.abc.rst | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 924d0b58d952fd..d1470c5a24528a 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -24,6 +24,85 @@ This module provides :term:`abstract base classes ` that can be used to test whether a class provides a particular interface; for 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. + +First, a newly written class can inherit directly from one 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 MySeq(Sequence): + 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:: + + >>> Sequence.register(MySeq) + + >>> issubclass(MySeq, Sequence) + True + >>> isinstance(MySeq(), Sequence) + True + +Second, existing classes and built-in classes can be registered as +"virtual subclasses" of the ABCs. So that users can rely on the +interface being present, the existing class should define all of the +abstract methods and mixin methods unless those methods are +automatically inferred from the rest of the API: + +.. testcode:: + + class MySeq: + def __init__(self): ... # Extra method not required by the ABC + def __getitem__(self, index): ... + def __len__(self): ... + def count(self, value): ... + def index(self, value): ... + +.. doctest:: + + >>> Sequence.register(MySeq) + + >>> issubclass(MySeq, Sequence) + True + >>> isinstance(MySeq(), Sequence) + True + +In this second example, :class:`~MySeq` does not need to define +``__contains__``, ``__iter__``, and ``__reversed__`` because the +:ref:`in-operator `, :term:`iteration ` logic, +and the :func:`reversed` function automatically fall back to using +``__getitem__`` and ``__len__``. + +Third, some simple interfaces are directly recognizable just by the +presence of the required methods: + +.. testcode:: + + class MyIterable: + def __iter__(self): ... + def __next__(next): ... + +.. doctest:: + + >>> issubclass(MySeq, Iterable) + True + >>> isinstance(MySeq(), Iterable) + True + +Complex interfaces do no support this last technique because an +interface is more than just the presence of method names. Interfaces +specify semanatics 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: From ef498c9606fd74c0f960ab8d03a06a9921747e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Tue, 7 Sep 2021 20:24:47 +0200 Subject: [PATCH 02/15] Make doctests pass --- Doc/library/collections.abc.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index d1470c5a24528a..a021e12b2e4c0c 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -14,7 +14,7 @@ .. testsetup:: * - from collections import * + from collections.abc import * import itertools __name__ = '' @@ -43,7 +43,7 @@ desired. Other methods may be added as needed: .. doctest:: >>> Sequence.register(MySeq) - + .MySeq'> >>> issubclass(MySeq, Sequence) True >>> isinstance(MySeq(), Sequence) @@ -67,7 +67,7 @@ automatically inferred from the rest of the API: .. doctest:: >>> Sequence.register(MySeq) - + .MySeq'> >>> issubclass(MySeq, Sequence) True >>> isinstance(MySeq(), Sequence) From 7d32007ea8645810eded40d5e3d52beb297d80eb Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 15:50:55 -0500 Subject: [PATCH 03/15] Note specifically which ABCs support method name checks --- Doc/library/collections.abc.rst | 134 +++++++++++++++++--------------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index a021e12b2e4c0c..1d3aac95f92318 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -113,67 +113,79 @@ The collections module offers the following :term:`ABCs `: .. tabularcolumns:: |l|L|L|L| -========================== ====================== ======================= ==================================================== -ABC Inherits from Abstract Methods Mixin Methods -========================== ====================== ======================= ==================================================== -:class:`Container` ``__contains__`` -:class:`Hashable` ``__hash__`` -:class:`Iterable` ``__iter__`` -:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__`` -:class:`Reversible` :class:`Iterable` ``__reversed__`` -:class:`Generator` :class:`Iterator` ``send``, ``throw`` ``close``, ``__iter__``, ``__next__`` -:class:`Sized` ``__len__`` -:class:`Callable` ``__call__`` -:class:`Collection` :class:`Sized`, ``__contains__``, - :class:`Iterable`, ``__iter__``, - :class:`Container` ``__len__`` - -:class:`Sequence` :class:`Reversible`, ``__getitem__``, ``__contains__``, ``__iter__``, ``__reversed__``, - :class:`Collection` ``__len__`` ``index``, and ``count`` - -:class:`MutableSequence` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods and - ``__setitem__``, ``append``, ``reverse``, ``extend``, ``pop``, - ``__delitem__``, ``remove``, and ``__iadd__`` - ``__len__``, - ``insert`` - -:class:`ByteString` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods - ``__len__`` - -:class:`Set` :class:`Collection` ``__contains__``, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, - ``__iter__``, ``__gt__``, ``__ge__``, ``__and__``, ``__or__``, - ``__len__`` ``__sub__``, ``__xor__``, and ``isdisjoint`` - -:class:`MutableSet` :class:`Set` ``__contains__``, Inherited :class:`Set` methods and - ``__iter__``, ``clear``, ``pop``, ``remove``, ``__ior__``, - ``__len__``, ``__iand__``, ``__ixor__``, and ``__isub__`` - ``add``, - ``discard`` - -:class:`Mapping` :class:`Collection` ``__getitem__``, ``__contains__``, ``keys``, ``items``, ``values``, - ``__iter__``, ``get``, ``__eq__``, and ``__ne__`` - ``__len__`` - -:class:`MutableMapping` :class:`Mapping` ``__getitem__``, Inherited :class:`Mapping` methods and - ``__setitem__``, ``pop``, ``popitem``, ``clear``, ``update``, - ``__delitem__``, and ``setdefault`` - ``__iter__``, - ``__len__`` - - -:class:`MappingView` :class:`Sized` ``__len__`` -:class:`ItemsView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`KeysView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ValuesView` :class:`MappingView`, ``__contains__``, ``__iter__`` - :class:`Collection` -:class:`Awaitable` ``__await__`` -:class:`Coroutine` :class:`Awaitable` ``send``, ``throw`` ``close`` -:class:`AsyncIterable` ``__aiter__`` -:class:`AsyncIterator` :class:`AsyncIterable` ``__anext__`` ``__aiter__`` -:class:`AsyncGenerator` :class:`AsyncIterator` ``asend``, ``athrow`` ``aclose``, ``__aiter__``, ``__anext__`` -========================== ====================== ======================= ==================================================== +============================== ====================== ======================= ==================================================== +ABC Inherits from Abstract Methods Mixin Methods +============================== ====================== ======================= ==================================================== +:class:`Container` [1]_ ``__contains__`` +:class:`Hashable` [1]_ ``__hash__`` +:class:`Iterable` [1]_ ``__iter__`` +:class:`Iterator` [1]_ :class:`Iterable` ``__next__`` ``__iter__`` +:class:`Reversible` [1]_ :class:`Iterable` ``__reversed__`` +:class:`Generator` [1]_ :class:`Iterator` ``send``, ``throw`` ``close``, ``__iter__``, ``__next__`` +:class:`Sized` [1]_ ``__len__`` +:class:`Callable` [1]_ ``__call__`` +:class:`Collection` [1]_ :class:`Sized`, ``__contains__``, + :class:`Iterable`, ``__iter__``, + :class:`Container` ``__len__`` + +:class:`Sequence` :class:`Reversible`, ``__getitem__``, ``__contains__``, ``__iter__``, ``__reversed__``, + :class:`Collection` ``__len__`` ``index``, and ``count`` + +:class:`MutableSequence` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods and + ``__setitem__``, ``append``, ``reverse``, ``extend``, ``pop``, + ``__delitem__``, ``remove``, and ``__iadd__`` + ``__len__``, + ``insert`` + +:class:`ByteString` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods + ``__len__`` + +:class:`Set` :class:`Collection` ``__contains__``, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, + ``__iter__``, ``__gt__``, ``__ge__``, ``__and__``, ``__or__``, + ``__len__`` ``__sub__``, ``__xor__``, and ``isdisjoint`` + +:class:`MutableSet` :class:`Set` ``__contains__``, Inherited :class:`Set` methods and + ``__iter__``, ``clear``, ``pop``, ``remove``, ``__ior__``, + ``__len__``, ``__iand__``, ``__ixor__``, and ``__isub__`` + ``add``, + ``discard`` + +:class:`Mapping` :class:`Collection` ``__getitem__``, ``__contains__``, ``keys``, ``items``, ``values``, + ``__iter__``, ``get``, ``__eq__``, and ``__ne__`` + ``__len__`` + +:class:`MutableMapping` :class:`Mapping` ``__getitem__``, Inherited :class:`Mapping` methods and + ``__setitem__``, ``pop``, ``popitem``, ``clear``, ``update``, + ``__delitem__``, and ``setdefault`` + ``__iter__``, + ``__len__`` + + +:class:`MappingView` :class:`Sized` ``__len__`` +:class:`ItemsView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`KeysView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ValuesView` :class:`MappingView`, ``__contains__``, ``__iter__`` + :class:`Collection` +:class:`Awaitable` [1]_ ``__await__`` +:class:`Coroutine` [1]_ :class:`Awaitable` ``send``, ``throw`` ``close`` +:class:`AsyncIterable` [1]_ ``__aiter__`` +:class:`AsyncIterator` [1]_ :class:`AsyncIterable` ``__anext__`` ``__aiter__`` +:class:`AsyncGenerator` [1]_ :class:`AsyncIterator` ``asend``, ``athrow`` ``aclose``, ``__aiter__``, ``__anext__`` +============================== ====================== ======================= ==================================================== + + +.. rubric:: Footnotes + +.. [1] This ABC overrides :meth:`object.__subclasshook__` to support + testing an interface verifying the required methods are present. + This only works for simple interfaces. More complex interfaces + require registration or direct subclassing. + + +Collections Abstract Base Classes -- Detailed Descriptions +---------------------------------------------------------- .. class:: Container From 4737fc5c486c943cf61b1e016a771cabf2852f12 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 15:55:49 -0500 Subject: [PATCH 04/15] Fix typo --- Doc/library/collections.abc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 1d3aac95f92318..6173d73d99b0ef 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -95,7 +95,7 @@ presence of the required methods: >>> isinstance(MySeq(), Iterable) True -Complex interfaces do no support this last technique because an +Complex interfaces do not support this last technique because an interface is more than just the presence of method names. Interfaces specify semanatics and relationships between methods that cannot be inferred solely from the presence of specific method names. For @@ -179,7 +179,7 @@ ABC Inherits from Abstract Methods Mi .. rubric:: Footnotes .. [1] This ABC overrides :meth:`object.__subclasshook__` to support - testing an interface verifying the required methods are present. + testing an interface by verifying the required methods are present. This only works for simple interfaces. More complex interfaces require registration or direct subclassing. From 8916fc89f58098033d440696fec4aedb2ae793e1 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 16:17:11 -0500 Subject: [PATCH 05/15] Add another footnote --- Doc/library/collections.abc.rst | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 6173d73d99b0ef..54a985745c1dc0 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -27,10 +27,10 @@ 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. -First, a newly written class can inherit directly from one 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: +First, 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:: @@ -79,8 +79,9 @@ In this second example, :class:`~MySeq` does not need to define and the :func:`reversed` function automatically fall back to using ``__getitem__`` and ``__len__``. -Third, some simple interfaces are directly recognizable just by the -presence of the required methods: +Third, some simple interfaces are directly recognizable by the presence +of the required methods (unless those methods have been set to +:const:`None`. .. testcode:: @@ -118,7 +119,7 @@ ABC Inherits from Abstract Methods Mi ============================== ====================== ======================= ==================================================== :class:`Container` [1]_ ``__contains__`` :class:`Hashable` [1]_ ``__hash__`` -:class:`Iterable` [1]_ ``__iter__`` +:class:`Iterable` [1]_ [2]_ ``__iter__`` :class:`Iterator` [1]_ :class:`Iterable` ``__next__`` ``__iter__`` :class:`Reversible` [1]_ :class:`Iterable` ``__reversed__`` :class:`Generator` [1]_ :class:`Iterator` ``send``, ``throw`` ``close``, ``__iter__``, ``__next__`` @@ -179,9 +180,16 @@ ABC Inherits from Abstract Methods Mi .. rubric:: Footnotes .. [1] This ABC overrides :meth:`object.__subclasshook__` to support - testing an interface by verifying the required methods are present. - This only works for simple interfaces. More complex interfaces - require registration or direct subclassing. + 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 From 768a6cca7de9062abf59f8918deeeb3c2ed7c3ee Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 16:20:21 -0500 Subject: [PATCH 06/15] Differentiate the first and second examples --- Doc/library/collections.abc.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 54a985745c1dc0..d6a3f8a4e2a813 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -57,7 +57,7 @@ automatically inferred from the rest of the API: .. testcode:: - class MySeq: + class SecondSeq: def __init__(self): ... # Extra method not required by the ABC def __getitem__(self, index): ... def __len__(self): ... @@ -66,14 +66,14 @@ automatically inferred from the rest of the API: .. doctest:: - >>> Sequence.register(MySeq) + >>> Sequence.register(SecondSeq) .MySeq'> - >>> issubclass(MySeq, Sequence) + >>> issubclass(SecondSeq, Sequence) True - >>> isinstance(MySeq(), Sequence) + >>> isinstance(SecondSeq(), Sequence) True -In this second example, :class:`~MySeq` does not need to define +In this example, :class:`~SecondSeq` does not need to define ``__contains__``, ``__iter__``, and ``__reversed__`` because the :ref:`in-operator `, :term:`iteration ` logic, and the :func:`reversed` function automatically fall back to using From 39e2f74c212750e50c7b2bea750415e5bf1f7769 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 16:23:13 -0500 Subject: [PATCH 07/15] Explicitly number the three subsections --- Doc/library/collections.abc.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index d6a3f8a4e2a813..e2b6fd419d64aa 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -27,7 +27,7 @@ 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. -First, a newly written class can inherit directly from one of the +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: @@ -49,11 +49,11 @@ overridden if desired. Other methods may be added as needed: >>> isinstance(MySeq(), Sequence) True -Second, existing classes and built-in classes can be registered as -"virtual subclasses" of the ABCs. So that users can rely on the -interface being present, the existing class should define all of the -abstract methods and mixin methods unless those methods are -automatically inferred from the rest of the API: +2) Existing classes and built-in classes can be registered as "virtual +subclasses" of the ABCs. So that users can rely on the interface being +present, the existing class should define all of the abstract methods +and mixin methods unless those methods are automatically inferred from +the rest of the API: .. testcode:: @@ -79,9 +79,9 @@ In this example, :class:`~SecondSeq` does not need to define and the :func:`reversed` function automatically fall back to using ``__getitem__`` and ``__len__``. -Third, some simple interfaces are directly recognizable by the presence -of the required methods (unless those methods have been set to -:const:`None`. +3) Some simple interfaces are directly recognizable by the presence of +the required methods (unless those methods have been set to +:const:`None`): .. testcode:: From 5fff432130e89713e302bc44206a6bb685820a6f Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 16:29:14 -0500 Subject: [PATCH 08/15] Add more inline comments for clarity --- Doc/library/collections.abc.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index e2b6fd419d64aa..a95402a1427b9e 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -34,11 +34,11 @@ overridden if desired. Other methods may be added as needed: .. testcode:: - class MySeq(Sequence): - 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 + class MySeq(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:: @@ -59,14 +59,14 @@ the rest of the API: class SecondSeq: def __init__(self): ... # Extra method not required by the ABC - def __getitem__(self, index): ... - def __len__(self): ... - def count(self, value): ... - def index(self, value): ... + def __getitem__(self, index): ... # Abstract method + def __len__(self): ... # Abstract method + def count(self, value): ... # Mixin method + def index(self, value): ... # Mixin method .. doctest:: - >>> Sequence.register(SecondSeq) + >>> Sequence.register(SecondSeq) # Register instead of inherit .MySeq'> >>> issubclass(SecondSeq, Sequence) True From 083b25b148be017eeac976246fbd50513ac38585 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 16:38:33 -0500 Subject: [PATCH 09/15] Improve wording --- Doc/library/collections.abc.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index a95402a1427b9e..b5e67f5dc375ed 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -50,15 +50,17 @@ overridden if desired. Other methods may be added as needed: True 2) Existing classes and built-in classes can be registered as "virtual -subclasses" of the ABCs. So that users can rely on the interface being -present, the existing class should define all of the abstract methods -and mixin methods unless those methods are automatically inferred from -the rest of the API: +subclasses" of the ABCs. Those classes should define the full API +including all of the abstract methods and all of 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 methods that are automatically inferred from the rest of +the API: .. testcode:: class SecondSeq: - def __init__(self): ... # Extra method not required by the ABC + 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 From 66cda8ff51a6c4a8121dbcc3705a5c0a684e6f90 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 16:49:39 -0500 Subject: [PATCH 10/15] More typo fixes and wordsmithing --- Doc/library/collections.abc.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index b5e67f5dc375ed..78324f9e090140 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -51,11 +51,11 @@ overridden if desired. Other methods may be added as needed: 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 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 methods that are automatically inferred from the rest of -the 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:: @@ -77,9 +77,9 @@ the API: In this example, :class:`~SecondSeq` does not need to define ``__contains__``, ``__iter__``, and ``__reversed__`` because the -:ref:`in-operator `, :term:`iteration ` logic, -and the :func:`reversed` function automatically fall back to using -``__getitem__`` and ``__len__``. +:ref:`in-operator `, the :term:`iteration ` +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 @@ -100,7 +100,7 @@ the required methods (unless those methods have been set to Complex interfaces do not support this last technique because an interface is more than just the presence of method names. Interfaces -specify semanatics and relationships between methods that cannot be +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 From 4b8f1ff116e0a764e926c295b5e12dad94b5d1d3 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 20:00:03 -0500 Subject: [PATCH 11/15] Fix doctests --- Doc/library/collections.abc.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 78324f9e090140..a623592459e4f7 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -69,7 +69,7 @@ of the API: .. doctest:: >>> Sequence.register(SecondSeq) # Register instead of inherit - .MySeq'> + .SecondSeq'> >>> issubclass(SecondSeq, Sequence) True >>> isinstance(SecondSeq(), Sequence) @@ -93,9 +93,9 @@ the required methods (unless those methods have been set to .. doctest:: - >>> issubclass(MySeq, Iterable) + >>> issubclass(MyIterable, Iterable) True - >>> isinstance(MySeq(), Iterable) + >>> isinstance(MyIterable(), Iterable) True Complex interfaces do not support this last technique because an From 3eb9ee1b272e2df3899448ea1fa72acc52736b09 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 21:58:03 -0500 Subject: [PATCH 12/15] Simpler class names. Extra comments. --- Doc/library/collections.abc.rst | 34 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index a623592459e4f7..20d0dab99404c8 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -34,7 +34,7 @@ overridden if desired. Other methods may be added as needed: .. testcode:: - class MySeq(Sequence): # Direct inheritance + 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 @@ -42,11 +42,9 @@ overridden if desired. Other methods may be added as needed: .. doctest:: - >>> Sequence.register(MySeq) - .MySeq'> - >>> issubclass(MySeq, Sequence) + >>> issubclass(C, Sequence) True - >>> isinstance(MySeq(), Sequence) + >>> isinstance(C(), Sequence) True 2) Existing classes and built-in classes can be registered as "virtual @@ -59,27 +57,27 @@ of the API: .. testcode:: - class SecondSeq: + 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:: - >>> Sequence.register(SecondSeq) # Register instead of inherit - .SecondSeq'> - >>> issubclass(SecondSeq, Sequence) + >>> issubclass(D, Sequence) True - >>> isinstance(SecondSeq(), Sequence) + >>> isinstance(D(), Sequence) True -In this example, :class:`~SecondSeq` does not need to define -``__contains__``, ``__iter__``, and ``__reversed__`` because the -:ref:`in-operator `, the :term:`iteration ` -logic, and the :func:`reversed` function automatically fall back to -using ``__getitem__`` and ``__len__``. +In this example, :class:`D` does not need to define ``__contains__``, +``__iter__``, and ``__reversed__`` because the :ref:`in-operator +`, the :term:`iteration ` 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 @@ -87,15 +85,15 @@ the required methods (unless those methods have been set to .. testcode:: - class MyIterable: + class E: def __iter__(self): ... def __next__(next): ... .. doctest:: - >>> issubclass(MyIterable, Iterable) + >>> issubclass(E, Iterable) True - >>> isinstance(MyIterable(), Iterable) + >>> isinstance(E(), Iterable) True Complex interfaces do not support this last technique because an From be475390595d370e64be4824859ea9fcec7b60b0 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 22:02:50 -0500 Subject: [PATCH 13/15] More wordsmithing --- Doc/library/collections.abc.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 20d0dab99404c8..273296aeb2a413 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -73,11 +73,11 @@ of the API: >>> isinstance(D(), Sequence) True -In this example, :class:`D` does not need to define ``__contains__``, -``__iter__``, and ``__reversed__`` because the :ref:`in-operator -`, the :term:`iteration ` logic, and the -:func:`reversed` function automatically fall back to using -``__getitem__`` and ``__len__``. +In this example, class :class:`D` does not need to define +``__contains__``, ``__iter__``, and ``__reversed__`` because the +:ref:`in-operator `, the :term:`iteration ` +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 @@ -179,7 +179,7 @@ ABC Inherits from Abstract Methods Mi .. rubric:: Footnotes -.. [1] This ABC overrides :meth:`object.__subclasshook__` to support +.. [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 From c12a287b64127924775c0a519eab04ec85165e00 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 Sep 2021 22:10:00 -0500 Subject: [PATCH 14/15] Add a new section divider --- Doc/library/collections.abc.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 273296aeb2a413..07df1873bba588 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -343,8 +343,10 @@ Collections Abstract Base Classes -- Detailed Descriptions .. 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:: size = None From 0cb4a9618f87c9c5019ec4e35908c90b6ad2945b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Wed, 8 Sep 2021 17:20:25 +0200 Subject: [PATCH 15/15] Add Blurb --- .../Documentation/2021-09-08-17-20-19.bpo-45024.dkNPNi.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Documentation/2021-09-08-17-20-19.bpo-45024.dkNPNi.rst diff --git a/Misc/NEWS.d/next/Documentation/2021-09-08-17-20-19.bpo-45024.dkNPNi.rst b/Misc/NEWS.d/next/Documentation/2021-09-08-17-20-19.bpo-45024.dkNPNi.rst new file mode 100644 index 00000000000000..e73d52b8cc514e --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2021-09-08-17-20-19.bpo-45024.dkNPNi.rst @@ -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.