diff options
author | Raymond Hettinger <rhettinger@users.noreply.github.com> | 2020-05-31 21:57:42 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-31 21:57:42 (GMT) |
commit | b7d79b4f36787874128c439d38397fe95c48429b (patch) | |
tree | 0ac2cf41b34220a380dcad803676896a41b4cc33 /Lib/collections/__init__.py | |
parent | 2b201369b435a4266bda5b895e3b615dbe28ea6e (diff) | |
download | cpython-b7d79b4f36787874128c439d38397fe95c48429b.zip cpython-b7d79b4f36787874128c439d38397fe95c48429b.tar.gz cpython-b7d79b4f36787874128c439d38397fe95c48429b.tar.bz2 |
bpo-40755: Add rich comparisons to Counter (GH-20548)
Diffstat (limited to 'Lib/collections/__init__.py')
-rw-r--r-- | Lib/collections/__init__.py | 122 |
1 files changed, 36 insertions, 86 deletions
diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 55fb46c..2acf672 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -691,6 +691,42 @@ class Counter(dict): if elem in self: super().__delitem__(elem) + def __eq__(self, other): + 'True if all counts agree. Missing counts are treated as zero.' + if not isinstance(other, Counter): + return NotImplemented + return all(self[e] == other[e] for c in (self, other) for e in c) + + def __ne__(self, other): + 'True if any counts disagree. Missing counts are treated as zero.' + if not isinstance(other, Counter): + return NotImplemented + return not self == other + + def __le__(self, other): + 'True if all counts in self are a subset of those in other.' + if not isinstance(other, Counter): + return NotImplemented + return all(self[e] <= other[e] for c in (self, other) for e in c) + + def __lt__(self, other): + 'True if all counts in self are a proper subset of those in other.' + if not isinstance(other, Counter): + return NotImplemented + return self <= other and self != other + + def __ge__(self, other): + 'True if all counts in self are a superset of those in other.' + if not isinstance(other, Counter): + return NotImplemented + return all(self[e] >= other[e] for c in (self, other) for e in c) + + def __gt__(self, other): + 'True if all counts in self are a proper superset of those in other.' + if not isinstance(other, Counter): + return NotImplemented + return self >= other and self != other + def __repr__(self): if not self: return '%s()' % self.__class__.__name__ @@ -886,92 +922,6 @@ class Counter(dict): self[elem] = other_count return self._keep_positive() - def isequal(self, other): - ''' Test whether counts agree exactly. - - Negative or missing counts are treated as zero. - - This is different than the inherited __eq__() method which - treats negative or missing counts as distinct from zero: - - >>> Counter(a=1, b=0).isequal(Counter(a=1)) - True - >>> Counter(a=1, b=0) == Counter(a=1) - False - - Logically equivalent to: +self == +other - ''' - if not isinstance(other, Counter): - other = Counter(other) - for elem in set(self) | set(other): - left = self[elem] - right = other[elem] - if left == right: - continue - if left < 0: - left = 0 - if right < 0: - right = 0 - if left != right: - return False - return True - - def issubset(self, other): - '''True if the counts in self are less than or equal to those in other. - - Negative or missing counts are treated as zero. - - Logically equivalent to: not self - (+other) - ''' - if not isinstance(other, Counter): - other = Counter(other) - for elem, count in self.items(): - other_count = other[elem] - if other_count < 0: - other_count = 0 - if count > other_count: - return False - return True - - def issuperset(self, other): - '''True if the counts in self are greater than or equal to those in other. - - Negative or missing counts are treated as zero. - - Logically equivalent to: not other - (+self) - ''' - if not isinstance(other, Counter): - other = Counter(other) - return other.issubset(self) - - def isdisjoint(self, other): - '''True if none of the elements in self overlap with those in other. - - Negative or missing counts are ignored. - - Logically equivalent to: not (+self) & (+other) - ''' - if not isinstance(other, Counter): - other = Counter(other) - for elem, count in self.items(): - if count > 0 and other[elem] > 0: - return False - return True - - # Rich comparison operators for multiset subset and superset tests - # have been deliberately omitted due to semantic conflicts with the - # existing inherited dict equality method. Subset and superset - # semantics ignore zero counts and require that p⊆q ∧ p⊇q ⇔ p=q; - # however, that would not be the case for p=Counter(a=1, b=0) - # and q=Counter(a=1) where the dictionaries are not equal. - - def _omitted(self, other): - raise TypeError( - 'Rich comparison operators have been deliberately omitted. ' - 'Use the isequal(), issubset(), and issuperset() methods instead.') - - __lt__ = __le__ = __gt__ = __ge__ = __lt__ = _omitted - ######################################################################## ### ChainMap |