Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 407c734

Browse files
authored
bpo-36057 Update docs and tests for ordering in collections.Counter [no behavior change] (#11962)
* Add tests for Counter order. No behavior change. * Update docs and tests * Fix doctest output and capitalization
1 parent 86f093f commit 407c734

3 files changed

Lines changed: 69 additions & 7 deletions

File tree

Doc/library/collections.rst

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,15 +268,20 @@ For example::
268268

269269
.. versionadded:: 3.1
270270

271+
.. versionchanged:: 3.7 As a :class:`dict` subclass, :class:`Counter`
272+
Inherited the capability to remember insertion order. Math operations
273+
on *Counter* objects also preserve order. Results are ordered
274+
according to when an element is first encountered in the left operand
275+
and then by the order encountered in the right operand.
271276

272277
Counter objects support three methods beyond those available for all
273278
dictionaries:
274279

275280
.. method:: elements()
276281

277282
Return an iterator over elements repeating each as many times as its
278-
count. Elements are returned in arbitrary order. If an element's count
279-
is less than one, :meth:`elements` will ignore it.
283+
count. Elements are returned in the order first encountered. If an
284+
element's count is less than one, :meth:`elements` will ignore it.
280285

281286
>>> c = Counter(a=4, b=2, c=0, d=-2)
282287
>>> sorted(c.elements())
@@ -287,10 +292,10 @@ For example::
287292
Return a list of the *n* most common elements and their counts from the
288293
most common to the least. If *n* is omitted or ``None``,
289294
:meth:`most_common` returns *all* elements in the counter.
290-
Elements with equal counts are ordered arbitrarily:
295+
Elements with equal counts are ordered in the order first encountered:
291296

292-
>>> Counter('abracadabra').most_common(3) # doctest: +SKIP
293-
[('a', 5), ('r', 2), ('b', 2)]
297+
>>> Counter('abracadabra').most_common(3)
298+
[('a', 5), ('b', 2), ('r', 2)]
294299

295300
.. method:: subtract([iterable-or-mapping])
296301

Lib/collections/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,8 @@ def most_common(self, n=None):
570570
'''List the n most common elements and their counts from the most
571571
common to the least. If n is None, then list all element counts.
572572
573-
>>> Counter('abcdeabcdabcaba').most_common(3)
574-
[('a', 5), ('b', 4), ('c', 3)]
573+
>>> Counter('abracadabra').most_common(3)
574+
[('a', 5), ('b', 2), ('r', 2)]
575575
576576
'''
577577
# Emulate Bag.sortedByCount from Smalltalk

Lib/test/test_collections.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,6 +1881,63 @@ def test_init(self):
18811881
self.assertRaises(TypeError, Counter, (), ())
18821882
self.assertRaises(TypeError, Counter.__init__)
18831883

1884+
def test_order_preservation(self):
1885+
# Input order dictates items() order
1886+
self.assertEqual(list(Counter('abracadabra').items()),
1887+
[('a', 5), ('b', 2), ('r', 2), ('c', 1), ('d', 1)])
1888+
# letters with same count: ^----------^ ^---------^
1889+
1890+
# Verify retention of order even when all counts are equal
1891+
self.assertEqual(list(Counter('xyzpdqqdpzyx').items()),
1892+
[('x', 2), ('y', 2), ('z', 2), ('p', 2), ('d', 2), ('q', 2)])
1893+
1894+
# Input order dictates elements() order
1895+
self.assertEqual(list(Counter('abracadabra simsalabim').elements()),
1896+
['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b','r',
1897+
'r', 'c', 'd', ' ', 's', 's', 'i', 'i', 'm', 'm', 'l'])
1898+
1899+
# Math operations order first by the order encountered in the left
1900+
# operand and then by the order encounted in the right operand.
1901+
ps = 'aaabbcdddeefggghhijjjkkl'
1902+
qs = 'abbcccdeefffhkkllllmmnno'
1903+
order = {letter: i for i, letter in enumerate(dict.fromkeys(ps + qs))}
1904+
def correctly_ordered(seq):
1905+
'Return true if the letters occur in the expected order'
1906+
positions = [order[letter] for letter in seq]
1907+
return positions == sorted(positions)
1908+
1909+
p, q = Counter(ps), Counter(qs)
1910+
self.assertTrue(correctly_ordered(+p))
1911+
self.assertTrue(correctly_ordered(-p))
1912+
self.assertTrue(correctly_ordered(p + q))
1913+
self.assertTrue(correctly_ordered(p - q))
1914+
self.assertTrue(correctly_ordered(p | q))
1915+
self.assertTrue(correctly_ordered(p & q))
1916+
1917+
p, q = Counter(ps), Counter(qs)
1918+
p += q
1919+
self.assertTrue(correctly_ordered(p))
1920+
1921+
p, q = Counter(ps), Counter(qs)
1922+
p -= q
1923+
self.assertTrue(correctly_ordered(p))
1924+
1925+
p, q = Counter(ps), Counter(qs)
1926+
p |= q
1927+
self.assertTrue(correctly_ordered(p))
1928+
1929+
p, q = Counter(ps), Counter(qs)
1930+
p &= q
1931+
self.assertTrue(correctly_ordered(p))
1932+
1933+
p, q = Counter(ps), Counter(qs)
1934+
p.update(q)
1935+
self.assertTrue(correctly_ordered(p))
1936+
1937+
p, q = Counter(ps), Counter(qs)
1938+
p.subtract(q)
1939+
self.assertTrue(correctly_ordered(p))
1940+
18841941
def test_update(self):
18851942
c = Counter()
18861943
c.update(self=42)

0 commit comments

Comments
 (0)