diff -r ec016ba862ba Lib/collections/__init__.py --- a/Lib/collections/__init__.py Tue Sep 16 20:35:55 2014 -0700 +++ b/Lib/collections/__init__.py Fri Oct 03 16:52:14 2014 -0700 @@ -775,20 +775,85 @@ class Counter(dict): >>> c Counter({'b': 1}) ''' for elem, count in self.items(): other_count = other[elem] if other_count < count: self[elem] = other_count return self._keep_positive() + # add partial order + # counter1 is less than counter2 if no element of counter one is greater + # than the corresponding element of counter2, and at least one matching + # element is smaller; missing elements count as zero + + def __lt__(self, other): + if not isinstance(other, Mapping): + return NotImplemented + found_strict_difference = False # Until challenged. + for element, count in self.items(): + other_count = other.get(element, 0) + if count > other_count: + return False + elif count < other_count: + found_strict_difference = True + if not found_strict_difference: + # equal to this point -- does other mapping have more keys? + if len(self) < len(other): + return True + return found_strict_difference + + def __le__(self, other): + if not isinstance(other, Mapping): + return NotImplemented + found_strict_difference = False # Until challenged. + for element, count in self.items(): + other_count = other.get(element, 0) + if count > other_count: + return False + if not found_strict_difference: + # equal to this point -- does other mapping have more keys? + if len(self) <= len(other): + return True + return found_strict_difference + + def __gt__(self, other): + if not isinstance(other, Mapping): + return NotImplemented + found_strict_difference = False # Until challenged. + for element, count in self.items(): + other_count = other.get(element, 0) + if count < other_count: + return False + elif count > other_count: + found_strict_difference = True + if not found_strict_difference: + # equal to this point -- does other mapping have fewer keys? + if len(self) > len(other): + return True + return found_strict_difference + + def __ge__(self, other): + if not isinstance(other, Mapping): + return NotImplemented + found_strict_difference = False # Until challenged. + for element, count in self.items(): + other_count = other.get(element, 0) + if count < other_count: + return False + if not found_strict_difference: + # equal to this point -- does other mapping have fewer keys? + if len(self) >= len(other): + return True + return found_strict_difference + ######################################################################## ### ChainMap (helper for configparser and string.Template) ######################################################################## class ChainMap(MutableMapping): ''' A ChainMap groups multiple dicts (or other mappings) together to create a single, updateable view. The underlying mappings are stored in a list. That list is public and can diff -r ec016ba862ba Lib/test/test_collections.py --- a/Lib/test/test_collections.py Tue Sep 16 20:35:55 2014 -0700 +++ b/Lib/test/test_collections.py Fri Oct 03 16:52:14 2014 -0700 @@ -12,20 +12,21 @@ import keyword import re import sys from collections import UserDict from collections import ChainMap from collections.abc import Hashable, Iterable, Iterator from collections.abc import Sized, Container, Callable from collections.abc import Set, MutableSet from collections.abc import Mapping, MutableMapping, KeysView, ItemsView from collections.abc import Sequence, MutableSequence from collections.abc import ByteString +import decimal ################################################################################ ### ChainMap (helper class for configparser and the string module) ################################################################################ class TestChainMap(unittest.TestCase): def test_basics(self): c = ChainMap() @@ -1219,20 +1220,68 @@ class TestCounter(unittest.TestCase): q = Counter(dict((elem, randrange(0, 2)) for elem in elements)) for counterop, setop in [ (Counter.__sub__, set.__sub__), (Counter.__or__, set.__or__), (Counter.__and__, set.__and__), ]: counter_result = counterop(p, q) set_result = setop(set(p.elements()), set(q.elements())) self.assertEqual(counter_result, dict.fromkeys(set_result, 1)) + def test_partial_order(self): + counter_0 = Counter() + counter_1 = Counter('c') + counter_2 = Counter('abc') + counter_3 = Counter('aabc') + counter_4 = Counter('abbc') + counter_5 = Counter('aabbcc') + + bad_d = {'a': 'a', 'b':'b', 'c':'c'} + + biggest = ( + (counter_5, (counter_4, counter_3, counter_2, counter_1, counter_0)), + (counter_4, (counter_2, counter_1, counter_0)), + (counter_3, (counter_2, counter_1, counter_0)), + (counter_2, (counter_1, )), + (counter_1, (counter_0, )), + ) + smallest = ( + (counter_0, (counter_1, counter_2, counter_3, counter_4, counter_5)), + (counter_1, (counter_2, counter_3, counter_4, counter_5)), + (counter_2, (counter_3, counter_4, counter_5)), + (counter_3, (counter_5, )), + (counter_4, (counter_5, )), + ) + for item, smaller_items in biggest: + for smaller_item in smaller_items: + self.assertFalse(item <= smaller_item) + self.assertFalse(item < smaller_item) + self.assertTrue(item >= smaller_item) + self.assertTrue(item > smaller_item) + self.assertTrue(item != smaller_item) + for item, bigger_items in smallest: + for bigger_item in bigger_items: + self.assertFalse(item >= bigger_item) + self.assertFalse(item > bigger_item) + self.assertTrue(item <= bigger_item) + self.assertTrue(item < bigger_item) + self.assertTrue(item != bigger_item) + for item in (counter_2, counter_3, counter_4, counter_5): + with self.assertRaises(TypeError): + item <= bad_d + with self.assertRaises(TypeError): + item < bad_d + with self.assertRaises(TypeError): + item >= bad_d + with self.assertRaises(TypeError): + item > bad_d + def test_inplace_operations(self): elements = 'abcd' 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)) q.update(h=1, i=-1, j=0) for inplace_op, regular_op in [ (Counter.__iadd__, Counter.__add__),