我会提出两个解决方案:
一个包装值本身,尽管这里只保证问题中的示例:其他Counter
操作不是:
>>> class ZeroAsEmptyMixin:
... _empty_val = None
...
... def __gt__(self, other):
... if isinstance(other, int) and other == 0:
... other = self._empty_val
... return super().__gt__(other)
...
... def __add__(self, other):
... if isinstance(other, int) and other == 0:
... other = self._empty_val
... return self.__class__(super().__add__(other))
...
...
>>> class mystr(ZeroAsEmptyMixin, str):
... _empty_val = str()
...
...
>>> class mylist(ZeroAsEmptyMixin, list):
... _empty_val = list()
...
...
>>>
>>> Counter({'a': mystr('hello '), 'b': mystr('B')}) + Counter({'a': mystr('world'), 'c': mystr('C')})
Counter({'a': 'hello world', 'c': 'C', 'b': 'B'})
>>> Counter({'a': mylist([1, 2]), 'b': mylist([3, 4])}) + Counter({'a': mylist([1, 1]), 'c': mylist([5, 6, 7])})
Counter({'c': [5, 6, 7], 'b': [3, 4], 'a': [1, 2, 1, 1]})
另一种方法是编写一个“Counter-like”的自定义类,子类化defaultdict
,再一次,只实现该__add__
方法。
>>> from collections import defaultdict
>>> from functools import wraps
>>>
>>> class MapReducer(defaultdict):
... @wraps(defaultdict.__init__)
... def __init__(self, *args, **kwargs):
... super().__init__(*args, **kwargs)
... self._empty_val = self.default_factory()
...
... def __add__(self, other):
... if not isinstance(other, self.__class__):
... return NotImplemented
... result = self.__class__(self.default_factory)
... for elem, val in self.items():
... newval = val + other[elem]
... if newval != self._empty_val:
... result[elem] = newval
... for elem, val in other.items():
... if elem not in self and val != self._empty_val:
... result[elem] = val
... return result
...
...
>>>
>>> strmp = lambda x: MapReducer(str, x)
>>> strmp({'a': 'hello ', 'b': 'B'}) + strmp({'a': 'world', 'c': 'C'})
MapReducer(<class 'str'>, {'a': 'hello world', 'b': 'B', 'c': 'C'})
>>> listmp = lambda x: MapReducer(list, x)
>>> listmp({'a': [1, 2], 'b': [3, 4]}) + listmp({'a': [1, 1], 'c': [5, 6, 7]})
MapReducer(<class 'list'>, {'a': [1, 2, 1, 1], 'b': [3, 4], 'c': [5, 6, 7]})