]> granicus.if.org Git - python/commitdiff
Add collections.Reversible. Patch by Ivan Levkivskyi. Fixes issue #25987.
authorGuido van Rossum <guido@dropbox.com>
Mon, 4 Apr 2016 17:59:29 +0000 (10:59 -0700)
committerGuido van Rossum <guido@dropbox.com>
Mon, 4 Apr 2016 17:59:29 +0000 (10:59 -0700)
Doc/library/collections.abc.rst
Doc/library/typing.rst
Lib/_collections_abc.py
Lib/test/test_collections.py
Lib/test/test_functools.py

index d9b93ad26273edc189e5111157e94fce761362d2..608641bad3a7f9a172b41102ae3526cd538406c9 100644 (file)
@@ -40,12 +40,13 @@ ABC                        Inherits from          Abstract Methods        Mixin
 :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:`Sequence`          :class:`Sized`,        ``__getitem__``,        ``__contains__``, ``__iter__``, ``__reversed__``,
-                           :class:`Iterable`,     ``__len__``             ``index``, and ``count``
+                           :class:`Reversible`,   ``__len__``             ``index``, and ``count``
                            :class:`Container`
 
 :class:`MutableSequence`   :class:`Sequence`      ``__getitem__``,        Inherited :class:`Sequence` methods and
@@ -107,6 +108,10 @@ ABC                        Inherits from          Abstract Methods        Mixin
    :meth:`~iterator.__next__` methods.  See also the definition of
    :term:`iterator`.
 
+.. class:: Reversible
+
+   ABC for classes that provide the :meth:`__reversed__` method.
+
 .. class:: Generator
 
    ABC for generator classes that implement the protocol defined in
index 12b5490b667e0c2237014faa3cc658de091c4cf0..1bd4b09da193f7d17879228b230cfc1585dd074e 100644 (file)
@@ -351,6 +351,10 @@ The module defines the following classes, functions and decorators:
 
     A generic version of the :class:`collections.abc.Iterator`.
 
+.. class:: Reversible(Iterable[T_co])
+
+    A generic version of the :class:`collections.abc.Reversible`.
+
 .. class:: SupportsInt
 
     An ABC with one abstract method ``__int__``.
@@ -369,11 +373,6 @@ The module defines the following classes, functions and decorators:
     An ABC with one abstract method ``__round__``
     that is covariant in its return type.
 
-.. class:: Reversible
-
-    An ABC with one abstract method ``__reversed__`` returning
-    an ``Iterator[T_co]``.
-
 .. class:: Container(Generic[T_co])
 
     A generic version of :class:`collections.abc.Container`.
@@ -394,7 +393,7 @@ The module defines the following classes, functions and decorators:
 
     A generic version of :class:`collections.abc.MutableMapping`.
 
-.. class:: Sequence(Sized, Iterable[T_co], Container[T_co])
+.. class:: Sequence(Sized, Reversible[T_co], Container[T_co])
 
     A generic version of :class:`collections.abc.Sequence`.
 
index f89bb6f04b5b1f7cbf2d0555d0d6e4a34221f48f..d3375847e2861cb4e20c981531a8412437001c0d 100644 (file)
@@ -10,7 +10,7 @@ from abc import ABCMeta, abstractmethod
 import sys
 
 __all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
-           "Hashable", "Iterable", "Iterator", "Generator",
+           "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
            "Sized", "Container", "Callable",
            "Set", "MutableSet",
            "Mapping", "MutableMapping",
@@ -240,6 +240,25 @@ Iterator.register(tuple_iterator)
 Iterator.register(zip_iterator)
 
 
+class Reversible(Iterable):
+
+    __slots__ = ()
+
+    @abstractmethod
+    def __reversed__(self):
+        return NotImplemented
+
+    @classmethod
+    def __subclasshook__(cls, C):
+        if cls is Reversible:
+            for B in C.__mro__:
+                if "__reversed__" in B.__dict__:
+                    if B.__dict__["__reversed__"] is not None:
+                        return True
+                    break
+        return NotImplemented
+
+
 class Generator(Iterator):
 
     __slots__ = ()
@@ -794,7 +813,7 @@ MutableMapping.register(dict)
 ### SEQUENCES ###
 
 
-class Sequence(Sized, Iterable, Container):
+class Sequence(Sized, Reversible, Container):
 
     """All the operations on a read-only sequence.
 
index 4c32e094f9e318ce32935b005577aa47bd5e5fc7..42024628c5e27dd60031254ffa01841da47c0261 100644 (file)
@@ -20,7 +20,7 @@ from collections import UserDict, UserString, UserList
 from collections import ChainMap
 from collections import deque
 from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable
-from collections.abc import Hashable, Iterable, Iterator, Generator
+from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible
 from collections.abc import Sized, Container, Callable
 from collections.abc import Set, MutableSet
 from collections.abc import Mapping, MutableMapping, KeysView, ItemsView
@@ -689,6 +689,31 @@ class TestOneTrickPonyABCs(ABCTestCase):
         self.validate_abstract_methods(Iterable, '__iter__')
         self.validate_isinstance(Iterable, '__iter__')
 
+    def test_Reversible(self):
+        # Check some non-reversibles
+        non_samples = [None, 42, 3.14, 1j, dict(), set(), frozenset()]
+        for x in non_samples:
+            self.assertNotIsInstance(x, Reversible)
+            self.assertFalse(issubclass(type(x), Reversible), repr(type(x)))
+        # Check some reversibles
+        samples = [tuple(), list()]
+        for x in samples:
+            self.assertIsInstance(x, Reversible)
+            self.assertTrue(issubclass(type(x), Reversible), repr(type(x)))
+        # Check also Mapping, MutableMapping, and Sequence
+        self.assertTrue(issubclass(Sequence, Reversible), repr(Sequence))
+        self.assertFalse(issubclass(Mapping, Reversible), repr(Mapping))
+        self.assertFalse(issubclass(MutableMapping, Reversible), repr(MutableMapping))
+        # Check direct subclassing
+        class R(Reversible):
+            def __iter__(self):
+                return iter(list())
+            def __reversed__(self):
+                return iter(list())
+        self.assertEqual(list(reversed(R())), [])
+        self.assertFalse(issubclass(float, R))
+        self.validate_abstract_methods(Reversible, '__reversed__', '__iter__')
+
     def test_Iterator(self):
         non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()]
         for x in non_samples:
@@ -842,14 +867,14 @@ class TestOneTrickPonyABCs(ABCTestCase):
         self.validate_isinstance(Callable, '__call__')
 
     def test_direct_subclassing(self):
-        for B in Hashable, Iterable, Iterator, Sized, Container, Callable:
+        for B in Hashable, Iterable, Iterator, Reversible, Sized, Container, Callable:
             class C(B):
                 pass
             self.assertTrue(issubclass(C, B))
             self.assertFalse(issubclass(int, C))
 
     def test_registration(self):
-        for B in Hashable, Iterable, Iterator, Sized, Container, Callable:
+        for B in Hashable, Iterable, Iterator, Reversible, Sized, Container, Callable:
             class C:
                 __hash__ = None  # Make sure it isn't hashable by default
             self.assertFalse(issubclass(C, B), B.__name__)
index 31930fc763a672b4a52a5563bd657534941286bd..a22d2e6609ceb0d04fd5317c279c08e9a6ae3262 100644 (file)
@@ -1516,7 +1516,7 @@ class TestSingleDispatch(unittest.TestCase):
             m = mro(D, bases)
             self.assertEqual(m, [D, c.MutableSequence, c.Sequence,
                                  c.defaultdict, dict, c.MutableMapping,
-                                 c.Mapping, c.Sized, c.Iterable, c.Container,
+                                 c.Mapping, c.Sized, c.Reversible, c.Iterable, c.Container,
                                  object])
 
         # Container and Callable are registered on different base classes and