[Python-checkins] cpython: Add collections.Reversible. Patch by Ivan Levkivskyi. Fixes issue #25987.

guido.van.rossum python-checkins at python.org
Mon Apr 4 14:04:21 EDT 2016


https://hg.python.org/cpython/rev/07f73360ea8e
changeset: 100846:07f73360ea8e
parent: 100844:0e576d094dc4
user: Guido van Rossum <guido at dropbox.com>
date: Mon Apr 04 10:59:29 2016 -0700
summary:
 Add collections.Reversible. Patch by Ivan Levkivskyi. Fixes issue #25987.
files:
 Doc/library/collections.abc.rst | 7 ++++-
 Doc/library/typing.rst | 11 +++----
 Lib/_collections_abc.py | 23 ++++++++++++++-
 Lib/test/test_collections.py | 31 ++++++++++++++++++--
 Lib/test/test_functools.py | 2 +-
 5 files changed, 61 insertions(+), 13 deletions(-)
diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst
--- a/Doc/library/collections.abc.rst
+++ b/Doc/library/collections.abc.rst
@@ -40,12 +40,13 @@
 :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 @@
 :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
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -351,6 +351,10 @@
 
 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 @@
 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 @@
 
 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`.
 
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -10,7 +10,7 @@
 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(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 @@
 ### SEQUENCES ###
 
 
-class Sequence(Sized, Iterable, Container):
+class Sequence(Sized, Reversible, Container):
 
 """All the operations on a read-only sequence.
 
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -20,7 +20,7 @@
 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 @@
 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 @@
 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__)
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -1516,7 +1516,7 @@
 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
-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /