Modification of collections.Counter/Dictionary (to not store negative values)
I found the Counter datatype from the collections module and it is almost perfect for my usecase. However I would like to implement a "simple" modification so it does not accept negative values and raises an error instead.
from collections import Counter
c = Counter({"a": 1})
c.update({"a": -2}) # this should raise an error since c[a] = -1
d = Counter({"b": -5}) # this should raise an error aswell
Of course I could just write a function to check all values and call it after each interaction with the counter, but this sounds like a bad idea to me.
If this is too much of a "do not ask others for code" question i apologize. I would be quite happy about specific info about the implementation of "Counter" aswell.
EDIT: I think an example with a standard dictionary would be perfectly fine. So the question would boil down to "How can i make a dictionary which raises an error if a value is a negative integer?"
I'd do this by just subclassing dict
rather than Counter
, since it gives you fewer methods to override:
>>> class PositiveDict(dict):
... __slots__ = ()
... def __init__(self, *args, **kwargs):
... super().__init__(*args, **kwargs)
... if any(v < 0 for v in self.values()):
... raise ValueError("no negatives allowed!")
... def __getitem__(self, key):
... return self.get(key, 0)
... def __setitem__(self, key, val):
... if val < 0:
... raise ValueError("no negatives allowed!")
... super().__setitem__(key, val)
... def update(self, other):
... if any(v < 0 for v in other.values()):
... raise ValueError("no negatives allowed!")
... super().update(other)
...
>>> d = PositiveDict({"a": 1})
>>> d["a"] -= 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in __setitem__
ValueError: no negatives allowed!
>>> d.update({"a": -2})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in update
ValueError: no negatives allowed!
>>> d["a"] += 1
>>> d["a"]
2
>>> d["b"]
0
Note that the __getitem__
implementation gives you the default value that you wanted from Counter. Everything else works by checking for a negative either before or after calling the super()
version of the method.