[Python-ideas] collections.Counter should implement __mul__, __rmul__
Tim Peters
tim.peters at gmail.com
Thu Apr 19 00:10:01 EDT 2018
[Serhiy]
>> Isn't everyone expect that x*2 == x + x? Isn't this the definition of
>> multiplication?
[Steven D'Aprano <steve at pearwood.info>]
> I can't think of any counter-examples, but it wouldn't surprise me even
> a tiny bit to learn of some.
Sure you can: explain -3.1 * -2.7 = 8.37 in terms of repeated addition ;-)
It's not even necessarily true in finite fields. For example, look at
the "+" and "*" tables for the 4-element finite field GF(4) here:
https://en.wikipedia.org/wiki/Finite_field#Field_with_four_elements
For every element x of that field, x+x = 0, so a*(1+a) = 1 in that
field can't be obtained by adding any number of a's (or any number of
1+a's).. Adding x to itself just gives x again if an odd number of
x's are added together, or 0 if an even number.
The sense in which it's always true in fields is technical: first,
x*y is guaranteed to be equivalent to repeated addition of x only if y
is "an integer"; and, more subtly, "integer" is defined as meaning the
result of adding the multiplicative identity any number of times to
the additive identity. In GF(4), under that definition, only 0 (the
additive identity) and 1 (the multiplicative identity (1) added to the
additive identity (0)) are "integers". Attempting to add 1 more times
just continues alternating between 0 and 1. `a` and `1+a` can't be
reached that way, so are not integers, and none of a*(1+a), a*a,
(1+a)*a, or (1+a)*(1+a) is the same as adding either operand any
number of times.
You could nevertheless define x*n as _meaning_ x+x+...+x (n times) for
cardinal numbers n, but then n is outside the field.
What's the application to Counter.__mul__? Not much ;-) The current
'+' and '-' map _pairs_ of Counters to a Counter. The proposed '*'
instead maps a Counter and "a scalar" to a Counter. It's always nice
if x*n is the same as repeated addition of x when n is a cardinal
integer, but because Counter.__add__ does special stuff specific to
the multiset interpretation, the proposed '*' doesn't always do the
same _unless_ the Counter is a legitimate multiset.
So it still works fine _in_ the multiset view, provided that you're
actually working with multiset Counters. But applications seeking to
mix Counters and scalars with "*" and "/" don't have multisets in mind
at all, so I'd be fine with `Counter * n` not being equivalent to
repeated multiset addition even when Counter is a legitimate multiset
and `n` is a cardinal. It's just gravy that it _is_ equivalent in
that case.
Where it breaks down is that, e.g,
>>> a = Counter(b=-100)
>>> a
Counter({'b': -100})
>>> a + a
Counter()
but the proposed a*2 would return Counter(a=-200). But, in that case,
`a` wasn't a legit multiset to begin with.
More information about the Python-ideas
mailing list