Implements a bag type that allows item counts to be negative.
| 9 | _DEBUG = True |
| 10 | |
| 11 | class IntegerBag(dict): |
| 12 | """Implements a bag type that allows item counts to be negative.""" |
| 13 | |
| 14 | __slots__ = [] |
| 15 | |
| 16 | def __init__(self, init={}): |
| 17 | """Initialize bag with optional contents. |
| 18 | |
| 19 | >>> b = Bag() #creates empty bag |
| 20 | >>> b |
| 21 | {} |
| 22 | >>> print(IntegerBag({1: -1, 2: 0, 3: -9})) |
| 23 | {1: -1, 3: -9} |
| 24 | |
| 25 | Can initialize with (key, count) list as in standard dict. |
| 26 | However, duplicate keys will accumulate counts: |
| 27 | >>> print(Bag([(1, 2), (2, 4), (1, 7)])) |
| 28 | {1: 9, 2: 4} |
| 29 | """ |
| 30 | if not init or isinstance(init, self.__class__): |
| 31 | dict.__init__(self, init) #values known to be good, use faster dict creation |
| 32 | else: #initializing with list or plain dict |
| 33 | dict.__init__(self) |
| 34 | if isinstance(init, dict): |
| 35 | for key, count in init.items(): |
| 36 | self[key] = count #will test invariants |
| 37 | else: #sequence may contain duplicates, so add to existing value, if any |
| 38 | for key, count in init: |
| 39 | self[key] += count |
| 40 | |
| 41 | def fromkeys(cls, iterable, count=1): |
| 42 | """Class method which creates bag from iterable adding optional count for each item. |
| 43 | |
| 44 | >>> b = Bag({'b': 2, 'c': 1, 'a': 3}) |
| 45 | >>> b2 = Bag.fromkeys(['a', 'b', 'c', 'b', 'a', 'a']) |
| 46 | >>> b3 = Bag.fromkeys("abacab") |
| 47 | >>> assert b == b2 == b3 |
| 48 | |
| 49 | >>> word_count = Bag.fromkeys("how much wood could a wood chuck chuck".split()) |
| 50 | >>> print(word_count) |
| 51 | {'a': 1, 'chuck': 2, 'could': 1, 'how': 1, 'much': 1, 'wood': 2} |
| 52 | |
| 53 | An optional count can be specified. Count added each time item is encountered. |
| 54 | >>> print(Bag.fromkeys("abacab", 5)) |
| 55 | {'a': 15, 'b': 10, 'c': 5} |
| 56 | """ |
| 57 | b = cls() |
| 58 | for key in iterable: #could just return b.__iadd__(iterable, count) |
| 59 | b[key] += count #perhaps slower than necessary but will simplify derived classes that override __setitem__() |
| 60 | return b |
| 61 | |
| 62 | fromkeys = classmethod(fromkeys) |
| 63 | |
| 64 | def update(self, items, count=1): |
| 65 | """Adds contents to bag from other mapping type or iterable. |
| 66 | |
| 67 | >>> ib = IntegerBag.fromkeys('abc') |
| 68 | >>> ib.update({'a': 2, 'b': 1, 'c': 0}) |
no test coverage detected