[Python-ideas] collections.Counter should implement __mul__, __rmul__

Steve Barnes gadgetsteve at live.co.uk
Mon Apr 16 01:22:54 EDT 2018



On 16/04/2018 06:07, Tim Peters wrote:
> [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`).
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
> 

Wouldn't it make sense to have the current counter behaviour, (negative 
counts not allowed), and also a counter that did allow negative values 
(my bank doesn't seem to have a problem with my balance being able to go 
below negative), and possibly at the same time a counter class that 
allowed fractional counts?

Then:
 >>>> x = Counter(a=1)
 >>>> y = Counter(a=2)
 >>>> x - y
 > Counter()
 >>>> x + -y
 > Counter({'a': 1})
BUT:
 >>>> x = Counter(a=1, allow_negative=True)
 >>>> y = Counter(a=2, allow_negative=True)
 >>>> x - y
 > Counter({'a': 1})
 >>>> x + -y
 > Counter({'a': 1})
Likewise for a Counter that was allowed to be fractional the result of 
some_counter / scalar would have (potentially) fractional results and 
one that did not would give floor results.

-- 
Steve (Gadget) Barnes
Any opinions in this message are my personal opinions and do not reflect 
those of my employer.

---
This email has been checked for viruses by AVG.
http://www.avg.com



More information about the Python-ideas mailing list