diff -r b7c0137cccbe Lib/collections/__init__.py --- a/Lib/collections/__init__.py Thu Mar 26 23:50:57 2015 +0100 +++ b/Lib/collections/__init__.py Fri Mar 27 10:24:52 2015 +0200 @@ -668,14 +668,8 @@ class Counter(dict): ''' if not isinstance(other, Counter): return NotImplemented - result = Counter() - for elem, count in self.items(): - newcount = count + other[elem] - if newcount > 0: - result[elem] = newcount - for elem, count in other.items(): - if elem not in self and count > 0: - result[elem] = count + result = Counter(self) + result += other return result def __sub__(self, other): @@ -688,8 +682,9 @@ class Counter(dict): if not isinstance(other, Counter): return NotImplemented result = Counter() + other_get = other.get for elem, count in self.items(): - newcount = count - other[elem] + newcount = count - other_get(elem, 0) if newcount > 0: result[elem] = newcount for elem, count in other.items(): @@ -706,15 +701,8 @@ class Counter(dict): ''' if not isinstance(other, Counter): return NotImplemented - result = Counter() - for elem, count in self.items(): - other_count = other[elem] - newcount = other_count if count < other_count else count - if newcount > 0: - result[elem] = newcount - for elem, count in other.items(): - if elem not in self and count > 0: - result[elem] = count + result = Counter(self) + result |= other return result def __and__(self, other): @@ -727,8 +715,9 @@ class Counter(dict): if not isinstance(other, Counter): return NotImplemented result = Counter() + other_get = other.get for elem, count in self.items(): - other_count = other[elem] + other_count = other_get(elem, 0) newcount = count if count < other_count else other_count if newcount > 0: result[elem] = newcount @@ -736,20 +725,36 @@ class Counter(dict): def __pos__(self): 'Adds an empty counter, effectively stripping negative and zero counts' - return self + Counter() + result = Counter(self) + result._keep_positive() + return result def __neg__(self): '''Subtracts from an empty counter. Strips positive and zero counts, and flips the sign on negative counts. ''' - return Counter() - self + result = Counter() + for elem, count in self.items(): + if count < 0: + result[elem] = 0 - count + return result def _keep_positive(self): '''Internal method to strip elements with a negative or zero count''' nonpositive = [elem for elem, count in self.items() if not count > 0] - for elem in nonpositive: - del self[elem] + if 2*len(nonpositive) < len(self): + # If most counts is positive (common case), it is faster to + # remove non-positive counts. + for elem in nonpositive: + del self[elem] + else: + # If most counts is non-positive, it is faster to create a dict + # with positive counts from scratch. + del nonpositive + positive = {elem: count for elem, count in self.items() if count > 0} + self.clear() + self.update(positive) return self def __iadd__(self, other): @@ -761,8 +766,9 @@ class Counter(dict): Counter({'b': 4, 'c': 2, 'a': 1}) ''' + self_get = self.get for elem, count in other.items(): - self[elem] += count + self[elem] = self_get(elem, 0) + count return self._keep_positive() def __isub__(self, other): @@ -774,8 +780,9 @@ class Counter(dict): Counter({'b': 2, 'a': 1}) ''' + self_get = self.get for elem, count in other.items(): - self[elem] -= count + self[elem] = self_get(elem, 0) - count return self._keep_positive() def __ior__(self, other): @@ -787,8 +794,9 @@ class Counter(dict): Counter({'b': 3, 'c': 2, 'a': 1}) ''' + self_get = self.get for elem, other_count in other.items(): - count = self[elem] + count = self_get(elem, 0) if other_count > count: self[elem] = other_count return self._keep_positive() @@ -802,8 +810,9 @@ class Counter(dict): Counter({'b': 1}) ''' + other_get = other.get for elem, count in self.items(): - other_count = other[elem] + other_count = other_get(elem, 0) if other_count < count: self[elem] = other_count return self._keep_positive()