summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2010-04-03 10:32:58 (GMT)
committerRaymond Hettinger <python@rcn.com>2010-04-03 10:32:58 (GMT)
commit9c01e441bb2589853446787d4150fe623050a1e1 (patch)
treefe8479098709faeb7bfd9c7e4b47091c6e0e0212
parent03d788dc4d6d399ba1092da5895edd7e6900345a (diff)
downloadcpython-9c01e441bb2589853446787d4150fe623050a1e1.zip
cpython-9c01e441bb2589853446787d4150fe623050a1e1.tar.gz
cpython-9c01e441bb2589853446787d4150fe623050a1e1.tar.bz2
Add a subtract() method to collections.Counter()
-rw-r--r--Doc/library/collections.rst11
-rw-r--r--Lib/collections.py28
-rw-r--r--Lib/test/test_collections.py10
-rw-r--r--Misc/NEWS2
4 files changed, 51 insertions, 0 deletions
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index 176975b..aa17a55 100644
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -214,6 +214,17 @@ For example::
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('r', 2), ('b', 2)]
+ .. method:: subtract([iterable-or-mapping])
+
+ Elements are subtracted from an *iterable* or from another *mapping*
+ (or counter). Like :meth:`dict.update` but subtracts counts instead
+ of replacing them. Both inputs and outputs may be zero or negative.
+
+ >>> c = Counter(a=4, b=2, c=0, d=-2)
+ >>> d = Counter(a=1, b=2, c=3, d=4)
+ >>> c.subtract(d)
+ Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
+
The usual dictionary methods are available for :class:`Counter` objects
except for two which work differently for counters.
diff --git a/Lib/collections.py b/Lib/collections.py
index 6ec062f..53e78f9 100644
--- a/Lib/collections.py
+++ b/Lib/collections.py
@@ -436,6 +436,34 @@ class Counter(dict):
if kwds:
self.update(kwds)
+ def subtract(self, iterable=None, **kwds):
+ '''Like dict.update() but subtracts counts instead of replacing them.
+ Counts can be reduced below zero. Both the inputs and outputs are
+ allowed to contain zero and negative counts.
+
+ Source can be an iterable, a dictionary, or another Counter instance.
+
+ >>> c = Counter('which')
+ >>> c.subtract('witch') # subtract elements from another iterable
+ >>> c.subtract(Counter('watch')) # subtract elements from another counter
+ >>> c['h'] # 2 in which, minus 1 in witch, minus 1 in watch
+ 0
+ >>> c['w'] # 1 in which, minus 1 in witch, minus 1 in watch
+ -1
+
+ '''
+ if iterable is not None:
+ if isinstance(iterable, Mapping):
+ self_get = self.get
+ for elem, count in iterable.items():
+ self[elem] = self_get(elem, 0) - count
+ else:
+ self_get = self.get
+ for elem in iterable:
+ self[elem] = self_get(elem, 0) - 1
+ if kwds:
+ self.subtract(kwds)
+
def copy(self):
'Like dict.copy() but returns a Counter instance instead of a dict.'
return Counter(self)
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 797b350..1bd49d9 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -661,6 +661,16 @@ class TestCounter(unittest.TestCase):
set_result = setop(set(p.elements()), set(q.elements()))
self.assertEqual(counter_result, dict.fromkeys(set_result, 1))
+ def test_subtract(self):
+ c = Counter(a=-5, b=0, c=5, d=10, e=15,g=40)
+ c.subtract(a=1, b=2, c=-3, d=10, e=20, f=30, h=-50)
+ self.assertEqual(c, Counter(a=-6, b=-2, c=8, d=0, e=-5, f=-30, g=40, h=50))
+ c = Counter(a=-5, b=0, c=5, d=10, e=15,g=40)
+ c.subtract(Counter(a=1, b=2, c=-3, d=10, e=20, f=30, h=-50))
+ self.assertEqual(c, Counter(a=-6, b=-2, c=8, d=0, e=-5, f=-30, g=40, h=50))
+ c = Counter('aaabbcd')
+ c.subtract('aaaabbcce')
+ self.assertEqual(c, Counter(a=-1, b=0, c=-1, d=1, e=-1))
class TestOrderedDict(unittest.TestCase):
diff --git a/Misc/NEWS b/Misc/NEWS
index 164655a..561f492 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -301,6 +301,8 @@ C-API
Library
-------
+- Added a subtract() method to collections.Counter().
+
- Issue #8233: When run as a script, py_compile.py optionally takes a single
argument `-` which tells it to read files to compile from stdin. Each line
is read on demand and the named file is compiled immediately. (Original