diff options
-rw-r--r-- | Doc/library/collections.rst | 20 | ||||
-rw-r--r-- | Lib/collections.py | 4 | ||||
-rw-r--r-- | Lib/test/test_collections.py | 17 |
3 files changed, 17 insertions, 24 deletions
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index dc1a70e4b3..a2af6b5309 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -265,16 +265,18 @@ Common patterns for working with :class:`Counter` objects:: c.items() # convert to a list of (elem, cnt) pairs Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs c.most_common()[:-n:-1] # n least common elements + c += Counter() # remove zero and negative counts Several multiset mathematical operations are provided for combining -:class:`Counter` objects. Multisets are like regular sets but allowed to +:class:`Counter` objects. Multisets are like regular sets but are allowed to contain repeated elements (with counts of one or more). Addition and subtraction combine counters by adding or subtracting the counts of corresponding elements. Intersection and union return the minimum and maximum -of corresponding counts:: +of corresponding counts. All four multiset operations exclude results with +zero or negative counts:: - >>> c = Counter({'a': 3, 'b': 1}) - >>> d = Counter({'a': 1, 'b': 2}) + >>> c = Counter(a=3, b=1) + >>> d = Counter(a=1, b=2) >>> c + d # add two counters together: c[x] + d[x] Counter({'a': 4, 'b': 3}) >>> c - d # subtract (keeping only positive counts) @@ -284,16 +286,6 @@ of corresponding counts:: >>> c | d # union: max(c[x], d[x]) Counter({'a': 3, 'b': 2}) -All four multiset operations produce only positive counts (negative and zero -results are skipped). If inputs include negative counts, addition will sum -both counts and then exclude non-positive results. The other three operations -are undefined for negative inputs:: - - >>> e = Counter(a=8, b=-2, c=0) - >>> e += Counter() # remove zero and negative counts - >>> e - Counter({'a': 8}) - .. seealso:: * `Bag class <http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html>`_ diff --git a/Lib/collections.py b/Lib/collections.py index a853076cb2..eb13f4d2fd 100644 --- a/Lib/collections.py +++ b/Lib/collections.py @@ -312,8 +312,8 @@ class Counter(dict): if not isinstance(other, Counter): return NotImplemented result = Counter() - for elem, count in self.iteritems(): - newcount = count - other[elem] + for elem in set(self) | set(other): + newcount = self[elem] - other[elem] if newcount > 0: result[elem] = newcount return result diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index ddf83985e0..47b093e9dd 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -457,18 +457,19 @@ class TestCounter(unittest.TestCase): for i in range(1000): # test random pairs of multisets p = Counter(dict((elem, randrange(-2,4)) for elem in elements)) + p.update(e=1, f=-1, g=0) q = Counter(dict((elem, randrange(-2,4)) for elem in elements)) - for counterop, numberop, defneg in [ - (Counter.__add__, lambda x, y: x+y if x+y>0 else 0, True), - (Counter.__sub__, lambda x, y: x-y if x-y>0 else 0, False), - (Counter.__or__, max, False), - (Counter.__and__, min, False), + q.update(h=1, i=-1, j=0) + for counterop, numberop in [ + (Counter.__add__, lambda x, y: max(0, x+y)), + (Counter.__sub__, lambda x, y: max(0, x-y)), + (Counter.__or__, lambda x, y: max(0,x,y)), + (Counter.__and__, lambda x, y: max(0, min(x,y))), ]: result = counterop(p, q) for x in elements: - # all except __add__ are undefined for negative inputs - if defneg or (p[x] >= 0 and q[x] >= 0): - self.assertEqual(numberop(p[x], q[x]), result[x]) + self.assertEqual(numberop(p[x], q[x]), result[x], + (counterop, x, p, q)) # verify that results exclude non-positive counts self.assert_(x>0 for x in result.values()) |