Skip to content

Commit 7a463ed

Browse files
committed
-
1 parent 8cf1d8a commit 7a463ed

File tree

2 files changed

+81
-25
lines changed

2 files changed

+81
-25
lines changed

source_py3/python_toolbox/nifty_collections/bagging.py

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import collections
99
import functools
1010

11+
from python_toolbox import math_tools
12+
1113
from .lazy_tuple import LazyTuple
1214
from .ordered_dict import OrderedDict
1315
from .frozen_dict_and_frozen_ordered_dict import FrozenDict, FrozenOrderedDict
@@ -21,6 +23,33 @@ def _count_elements(mapping, iterable):
2123
mapping_get = mapping.get
2224
for element in iterable:
2325
mapping[element] = mapping_get(element, 0) + 1
26+
27+
28+
class _NO_DEFAULT:
29+
pass
30+
31+
class _ZeroCountAttempted(Exception):
32+
pass
33+
34+
35+
def _process_count(count):
36+
if not math_tools.is_integer(count):
37+
raise TypeError(
38+
'You passed %s as the count of %s, while a `Bag` can only handle '
39+
'integer counts.' % (count, key)
40+
)
41+
if count < 0:
42+
raise TypeError(
43+
"You passed %s as the count of %s, while `Bag` doesn't support "
44+
"negative amounts." % (count, key)
45+
)
46+
47+
if count == 0:
48+
raise _ZeroCountAttempted
49+
50+
return int(count)
51+
52+
2453

2554
class _BaseBagMixin:
2655
'''Mixin for `FrozenBag` and `FrozenOrderedBag`.'''
@@ -32,23 +61,10 @@ def __init__(self, iterable={}):
3261

3362
if isinstance(iterable, collections.Mapping):
3463
for key, value, in iterable.items():
35-
if not math_tools.is_integer(value):
36-
raise TypeError(
37-
'You passed %s as the count of %s, while '
38-
'a `Bag` can only handle integer counts.' %
39-
(value, key)
40-
)
41-
if value < 0:
42-
raise TypeError(
43-
"You passed %s as the count of %s, while "
44-
"`Bag` doesn't support negative amounts." %
45-
(value, key)
46-
)
47-
48-
if value == 0:
64+
try:
65+
self._dict[key] = _process_count(value)
66+
except _ZeroCountAttempted:
4967
continue
50-
51-
self._dict[key] = int(value)
5268
else:
5369
_count_elements(self._dict, iterable)
5470

@@ -291,8 +307,52 @@ def __repr__(self):
291307

292308
class _MutableBagMixin(_BaseBagMixin):
293309
# blocktodo: ensure all mutable methods, like __iadd__ and everything
294-
pass
310+
311+
def __setitem__(self, i, count):
312+
try:
313+
super().__setitem__(i, _process_count(count))
314+
except _ZeroCountAttempted:
315+
del self[i]
316+
317+
318+
def __imod__(self, modulo):
319+
for key in tuple(self.keys()):
320+
self[key] %= modulo
321+
return self
322+
323+
def setdefault(self, key, default):
324+
current_count = self[key]
325+
if current_count > 0:
326+
return current_count
327+
else:
328+
self[key] = default
329+
return default
330+
331+
def __delitem__(self, key):
332+
# We're making `__delitem__` not raise an exception on missing or
333+
# zero-count elements because we're automatically deleting zero-count
334+
# elements even though they seem to exist from the outside, so we're
335+
# avoiding raising exceptions where someone would try to explicitly
336+
# delete them.
337+
try:
338+
del self._dict[key]
339+
except KeyError:
340+
pass
295341

342+
def pop(self, key, default=_NO_DEFAULT):
343+
'''D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
344+
If key is not found, d is returned if given, otherwise KeyError is raised.
345+
'''
346+
value = self[key]
347+
if value == 0 and default is not _NO_DEFAULT:
348+
return default
349+
else:
350+
del self[key]
351+
return value
352+
353+
354+
355+
296356
class _OrderedBagMixin(Ordered):
297357
def __repr__(self):
298358
if not self:

source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def test_mutating(self):
209209

210210
bag = self.bag_type('abracadabra')
211211
bag += bag
212-
assert bag == self.bag_type('abracadabr' * 2)
212+
assert bag == self.bag_type('abracadabra' * 2)
213213

214214
bag = self.bag_type('abracadabra')
215215
bag -= bag
@@ -223,10 +223,6 @@ def test_mutating(self):
223223
bag['a'] = 7
224224
assert bag == self.bag_type('abracadabra' + 'aa')
225225

226-
bag = self.bag_type('abracadabra')
227-
bag.set('a', 7)
228-
assert bag == self.bag_type('abracadabra' + 'aa')
229-
230226
bag = self.bag_type('abracadabra')
231227
assert bag.setdefault('a', 7) == 5
232228
assert bag == self.bag_type('abracadabra')
@@ -236,7 +232,9 @@ def test_mutating(self):
236232
assert bag == self.bag_type('abracadabra' + 'x' * 7)
237233

238234
bag = self.bag_type('abracadabra')
239-
assert bag.pop('a', 7) == 7
235+
assert bag.pop('a', 7) == 5
236+
assert bag == self.bag_type('brcdbr')
237+
assert bag.pop('x', 7) == 7
240238
assert bag == self.bag_type('brcdbr')
241239

242240
bag = self.bag_type('abracadabra')
@@ -289,8 +287,6 @@ def test_mutating(bag_type):
289287
bag %= bag
290288
with cute_testing.RaiseAssertor(TypeError):
291289
bag['a'] = 7
292-
with cute_testing.RaiseAssertor(TypeError):
293-
bag.set('a', 7)
294290
with cute_testing.RaiseAssertor(TypeError):
295291
bag.setdefault('a', 7)
296292
with cute_testing.RaiseAssertor(TypeError):

0 commit comments

Comments
 (0)