Skip to content

Commit f066694

Browse files
author
Guido van Rossum
committed
Issue 27598: Add Collections to collections.abc.
Patch by Ivan Levkivskyi, docs by Neil Girdhar.
1 parent 9ff4fb3 commit f066694

5 files changed

Lines changed: 132 additions & 21 deletions

File tree

Doc/library/collections.abc.rst

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ ABC Inherits from Abstract Methods Mixin
4545
:class:`Generator` :class:`Iterator` ``send``, ``throw`` ``close``, ``__iter__``, ``__next__``
4646
:class:`Sized` ``__len__``
4747
:class:`Callable` ``__call__``
48+
:class:`Collection` :class:`Sized`, ``__contains__``,
49+
:class:`Iterable`, ``__iter__``,
50+
:class:`Container` ``__len__``
4851

49-
:class:`Sequence` :class:`Sized`, ``__getitem__``, ``__contains__``, ``__iter__``, ``__reversed__``,
50-
:class:`Reversible`, ``__len__`` ``index``, and ``count``
51-
:class:`Container`
52+
:class:`Sequence` :class:`Reversible`, ``__getitem__``, ``__contains__``, ``__iter__``, ``__reversed__``,
53+
:class:`Collection` ``__len__`` ``index``, and ``count``
5254

5355
:class:`MutableSequence` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods and
5456
``__setitem__``, ``append``, ``reverse``, ``extend``, ``pop``,
@@ -59,19 +61,19 @@ ABC Inherits from Abstract Methods Mixin
5961
:class:`ByteString` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods
6062
``__len__``
6163

62-
:class:`Set` :class:`Sized`, ``__contains__``, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``,
63-
:class:`Iterable`, ``__iter__``, ``__gt__``, ``__ge__``, ``__and__``, ``__or__``,
64-
:class:`Container` ``__len__`` ``__sub__``, ``__xor__``, and ``isdisjoint``
64+
:class:`Set` :class:`Collection` ``__contains__``, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``,
65+
``__iter__``, ``__gt__``, ``__ge__``, ``__and__``, ``__or__``,
66+
``__len__`` ``__sub__``, ``__xor__``, and ``isdisjoint``
6567

6668
:class:`MutableSet` :class:`Set` ``__contains__``, Inherited :class:`Set` methods and
6769
``__iter__``, ``clear``, ``pop``, ``remove``, ``__ior__``,
6870
``__len__``, ``__iand__``, ``__ixor__``, and ``__isub__``
6971
``add``,
7072
``discard``
7173

72-
:class:`Mapping` :class:`Sized`, ``__getitem__``, ``__contains__``, ``keys``, ``items``, ``values``,
73-
:class:`Iterable`, ``__iter__``, ``get``, ``__eq__``, and ``__ne__``
74-
:class:`Container` ``__len__``
74+
:class:`Mapping` :class:`Collection` ``__getitem__``, ``__contains__``, ``keys``, ``items``, ``values``,
75+
``__iter__``, ``get``, ``__eq__``, and ``__ne__``
76+
``__len__``
7577

7678
:class:`MutableMapping` :class:`Mapping` ``__getitem__``, Inherited :class:`Mapping` methods and
7779
``__setitem__``, ``pop``, ``popitem``, ``clear``, ``update``,
@@ -106,6 +108,12 @@ ABC Inherits from Abstract Methods Mixin
106108
ABC for classes that provide the :meth:`__iter__` method.
107109
See also the definition of :term:`iterable`.
108110

111+
.. class:: Collection
112+
113+
ABC for sized iterable container classes.
114+
115+
.. versionadded:: 3.6
116+
109117
.. class:: Iterator
110118

111119
ABC for classes that provide the :meth:`~iterator.__iter__` and

Lib/_collections_abc.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
1313
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
14-
"Sized", "Container", "Callable",
14+
"Sized", "Container", "Callable", "Collection",
1515
"Set", "MutableSet",
1616
"Mapping", "MutableMapping",
1717
"MappingView", "KeysView", "ItemsView", "ValuesView",
@@ -326,6 +326,15 @@ def __subclasshook__(cls, C):
326326
return _check_methods(C, "__contains__")
327327
return NotImplemented
328328

329+
class Collection(Sized, Iterable, Container):
330+
331+
__slots__ = ()
332+
333+
@classmethod
334+
def __subclasshook__(cls, C):
335+
if cls is Collection:
336+
return _check_methods(C, "__len__", "__iter__", "__contains__")
337+
return NotImplemented
329338

330339
class Callable(metaclass=ABCMeta):
331340

@@ -345,7 +354,7 @@ def __subclasshook__(cls, C):
345354
### SETS ###
346355

347356

348-
class Set(Sized, Iterable, Container):
357+
class Set(Collection):
349358

350359
"""A set is a finite, iterable container.
351360
@@ -570,7 +579,7 @@ def __isub__(self, it):
570579
### MAPPINGS ###
571580

572581

573-
class Mapping(Sized, Iterable, Container):
582+
class Mapping(Collection):
574583

575584
__slots__ = ()
576585

@@ -794,7 +803,7 @@ def setdefault(self, key, default=None):
794803
### SEQUENCES ###
795804

796805

797-
class Sequence(Sized, Reversible, Container):
806+
class Sequence(Reversible, Collection):
798807

799808
"""All the operations on a read-only sequence.
800809

Lib/test/test_collections.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from collections import deque
2222
from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable
2323
from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible
24-
from collections.abc import Sized, Container, Callable
24+
from collections.abc import Sized, Container, Callable, Collection
2525
from collections.abc import Set, MutableSet
2626
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
2727
from collections.abc import Sequence, MutableSequence
@@ -771,6 +771,94 @@ class RevRevBlocked(Rev):
771771
self.assertFalse(issubclass(RevRevBlocked, Reversible))
772772
self.assertFalse(isinstance(RevRevBlocked(), Reversible))
773773

774+
def test_Collection(self):
775+
# Check some non-collections
776+
non_collections = [None, 42, 3.14, 1j, lambda x: 2*x]
777+
for x in non_collections:
778+
self.assertNotIsInstance(x, Collection)
779+
self.assertFalse(issubclass(type(x), Collection), repr(type(x)))
780+
# Check some non-collection iterables
781+
non_col_iterables = [_test_gen(), iter(b''), iter(bytearray()),
782+
(x for x in []), dict().values()]
783+
for x in non_col_iterables:
784+
self.assertNotIsInstance(x, Collection)
785+
self.assertFalse(issubclass(type(x), Collection), repr(type(x)))
786+
# Check some collections
787+
samples = [set(), frozenset(), dict(), bytes(), str(), tuple(),
788+
list(), dict().keys(), dict().items()]
789+
for x in samples:
790+
self.assertIsInstance(x, Collection)
791+
self.assertTrue(issubclass(type(x), Collection), repr(type(x)))
792+
# Check also Mapping, MutableMapping, etc.
793+
self.assertTrue(issubclass(Sequence, Collection), repr(Sequence))
794+
self.assertTrue(issubclass(Mapping, Collection), repr(Mapping))
795+
self.assertTrue(issubclass(MutableMapping, Collection),
796+
repr(MutableMapping))
797+
self.assertTrue(issubclass(Set, Collection), repr(Set))
798+
self.assertTrue(issubclass(MutableSet, Collection), repr(MutableSet))
799+
self.assertTrue(issubclass(Sequence, Collection), repr(MutableSet))
800+
# Check direct subclassing
801+
class Col(Collection):
802+
def __iter__(self):
803+
return iter(list())
804+
def __len__(self):
805+
return 0
806+
def __contains__(self, item):
807+
return False
808+
class DerCol(Col): pass
809+
self.assertEqual(list(iter(Col())), [])
810+
self.assertFalse(issubclass(list, Col))
811+
self.assertFalse(issubclass(set, Col))
812+
self.assertFalse(issubclass(float, Col))
813+
self.assertEqual(list(iter(DerCol())), [])
814+
self.assertFalse(issubclass(list, DerCol))
815+
self.assertFalse(issubclass(set, DerCol))
816+
self.assertFalse(issubclass(float, DerCol))
817+
self.validate_abstract_methods(Collection, '__len__', '__iter__',
818+
'__contains__')
819+
# Check sized container non-iterable (which is not Collection) etc.
820+
class ColNoIter:
821+
def __len__(self): return 0
822+
def __contains__(self, item): return False
823+
class ColNoSize:
824+
def __iter__(self): return iter([])
825+
def __contains__(self, item): return False
826+
class ColNoCont:
827+
def __iter__(self): return iter([])
828+
def __len__(self): return 0
829+
self.assertFalse(issubclass(ColNoIter, Collection))
830+
self.assertFalse(isinstance(ColNoIter(), Collection))
831+
self.assertFalse(issubclass(ColNoSize, Collection))
832+
self.assertFalse(isinstance(ColNoSize(), Collection))
833+
self.assertFalse(issubclass(ColNoCont, Collection))
834+
self.assertFalse(isinstance(ColNoCont(), Collection))
835+
# Check None blocking
836+
class SizeBlock:
837+
def __iter__(self): return iter([])
838+
def __contains__(self): return False
839+
__len__ = None
840+
class IterBlock:
841+
def __len__(self): return 0
842+
def __contains__(self): return True
843+
__iter__ = None
844+
self.assertFalse(issubclass(SizeBlock, Collection))
845+
self.assertFalse(isinstance(SizeBlock(), Collection))
846+
self.assertFalse(issubclass(IterBlock, Collection))
847+
self.assertFalse(isinstance(IterBlock(), Collection))
848+
# Check None blocking in subclass
849+
class ColImpl:
850+
def __iter__(self):
851+
return iter(list())
852+
def __len__(self):
853+
return 0
854+
def __contains__(self, item):
855+
return False
856+
class NonCol(ColImpl):
857+
__contains__ = None
858+
self.assertFalse(issubclass(NonCol, Collection))
859+
self.assertFalse(isinstance(NonCol(), Collection))
860+
861+
774862
def test_Iterator(self):
775863
non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()]
776864
for x in non_samples:

Lib/test/test_functools.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,13 +1548,15 @@ def test_compose_mro(self):
15481548
bases = [c.Sequence, c.MutableMapping, c.Mapping, c.Set]
15491549
for haystack in permutations(bases):
15501550
m = mro(dict, haystack)
1551-
self.assertEqual(m, [dict, c.MutableMapping, c.Mapping, c.Sized,
1552-
c.Iterable, c.Container, object])
1551+
self.assertEqual(m, [dict, c.MutableMapping, c.Mapping,
1552+
c.Collection, c.Sized, c.Iterable,
1553+
c.Container, object])
15531554
bases = [c.Container, c.Mapping, c.MutableMapping, c.OrderedDict]
15541555
for haystack in permutations(bases):
15551556
m = mro(c.ChainMap, haystack)
15561557
self.assertEqual(m, [c.ChainMap, c.MutableMapping, c.Mapping,
1557-
c.Sized, c.Iterable, c.Container, object])
1558+
c.Collection, c.Sized, c.Iterable,
1559+
c.Container, object])
15581560

15591561
# If there's a generic function with implementations registered for
15601562
# both Sized and Container, passing a defaultdict to it results in an
@@ -1575,9 +1577,9 @@ class D(c.defaultdict):
15751577
bases = [c.MutableSequence, c.MutableMapping]
15761578
for haystack in permutations(bases):
15771579
m = mro(D, bases)
1578-
self.assertEqual(m, [D, c.MutableSequence, c.Sequence,
1579-
c.defaultdict, dict, c.MutableMapping,
1580-
c.Mapping, c.Sized, c.Reversible, c.Iterable, c.Container,
1580+
self.assertEqual(m, [D, c.MutableSequence, c.Sequence, c.Reversible,
1581+
c.defaultdict, dict, c.MutableMapping, c.Mapping,
1582+
c.Collection, c.Sized, c.Iterable, c.Container,
15811583
object])
15821584

15831585
# Container and Callable are registered on different base classes and
@@ -1590,7 +1592,8 @@ def __call__(self):
15901592
for haystack in permutations(bases):
15911593
m = mro(C, haystack)
15921594
self.assertEqual(m, [C, c.Callable, c.defaultdict, dict, c.Mapping,
1593-
c.Sized, c.Iterable, c.Container, object])
1595+
c.Collection, c.Sized, c.Iterable,
1596+
c.Container, object])
15941597

15951598
def test_register_abc(self):
15961599
c = collections

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ Core and Builtins
126126
Library
127127
-------
128128

129+
- Issue 27598: Add Collections to collections.abc.
130+
Patch by Ivan Levkivskyi, docs by Neil Girdhar.
131+
129132
- Issue #25958: Support "anti-registration" of special methods from
130133
various ABCs, like __hash__, __iter__ or __len__. All these (and
131134
several more) can be set to None in an implementation class and the

0 commit comments

Comments
 (0)