Commit 16ca06b8 authored by Guido van Rossum's avatar Guido van Rossum

Add collections.Reversible. Patch by Ivan Levkivskyi. Fixes issue #25987.

parent 9ad76460
......@@ -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
......
......@@ -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`.
......
......@@ -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.
......
......@@ -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__)
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment