: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
: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
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__``.
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`.
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`.
import sys
__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
- "Hashable", "Iterable", "Iterator", "Generator",
+ "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
"Sized", "Container", "Callable",
"Set", "MutableSet",
"Mapping", "MutableMapping",
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__ = ()
### SEQUENCES ###
-class Sequence(Sized, Iterable, Container):
+class Sequence(Sized, Reversible, Container):
"""All the operations on a read-only sequence.
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
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:
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__)