[Python-ideas] collections.Counter should implement __mul__, __rmul__
Tim Peters
tim.peters at gmail.com
Mon Apr 16 01:07:24 EDT 2018
[Peter Norvig]
> For most types that implement __add__, `x + x` is equal to `2 * x`.
>
> That is true for all numbers, list, tuple, str, timedelta, etc. -- but not
> for collections.Counter. I can add two Counters, but I can't multiply one
> by a scalar. That seems like an oversight.
>
> ...
> Here's an implementation:
>
> def __mul__(self, scalar):
> "Multiply each entry by a scalar."
> result = Counter()
> for key in self:
> result[key] = self[key] * scalar
> return result
>
> def __rmul__(self, scalar):
> "Multiply each entry by a scalar."
> result = Counter()
> for key in self:
> result[key] = scalar * self[key]
> return result
Adding Counter * integer doesn't bother me a bit, but the definition
of what that should compute isn't obvious. In particular, that
implementation doesn't preserve that `x+x == 2*x` if x has any
negative values:
>>> x = Counter(a=-1)
>>> x
Counter({'a': -1})
>>> x+x
Counter()
It would be strange if x+x != 2*x, and if x*-1 != -x:
>>> y = Counter(a=1)
>>> y
Counter({'a': 1})
>>> -y
Counter()
Etc.
Then again, it's already the case that, e.g., x-y isn't always the
same as x + -y:
>>> x = Counter(a=1)
>>> y = Counter(a=2)
>>> x - y
Counter()
>>> x + -y
Counter({'a': 1})
So screw obvious formal identities ;-)
I'm not clear on why "+" and "-" discard keys with values <= 0 to
begin with. For "-" it's natural enough viewing "-" as being multiset
difference, but for "+"? That's just made up ;-)
In any case, despite the oddities, I think your implementation would
be least surprising overall (ignore the sign of the resulting values).
At least for Counters that actually make sense as multisets (have no
values <= 0), and for a positive integer multiplier `n > 0`, it does
preserve that `x*n` = `x + x + ... + x` (with `n` instances of `x`).
More information about the Python-ideas
mailing list