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

Wes Turner wes.turner at gmail.com
Wed Apr 18 15:33:57 EDT 2018


The use cases these TypeErrors would exclude include weightings (whether
from a generator without an intermediate tuple/list or from a dict) where
the need is to do elementwise multiplication:

if len(self) != len(other):
    raise ValueError("tensors are multiplicable")
if self.keys() != other.keys():
   #odict
if set(self.keys()).difference(other.keys()):
   # is this contract
   # is it this function's responsibility to check it
for (k1, v1), (k2, v2) in zip(self.items(), other.items()):
   self[k1] = v1*v2

At which point we might as well just introduce a sparse labeled array type
that's interface-compatible with np.array and/or pd.Series(index=)
with a @classmethod Counter initializer that works like
collections.Counter. (see links above)

On Wednesday, April 18, 2018, Tim Peters <tim.peters at gmail.com> wrote:

> [Raymond]
> >> I've started working on an implementation and several choices arise:
> >>
> >> 1) Reject scalar with a TypeError if scalar is a Counter
> >> 2) Reject scalar with a TypeError if scalar is a Mapping
> >> 3) Reject scalar with a TypeError if scalar is a Collection
> >> 4) Reject scalar with a TypeError if scalar is Sized (has a __len__
> >> method).
>
> [Petr Viktorin <encukou at gmail.com>]
> > Why is Iterable (__iter__) not on the list?
> >
> > (Apologies if I missed this somewhere in the conversation.)
>
> I believe Raymond implicitly meant "test one of the above", not "test
> all of the above", and he's leaning toward Sized alone.
>
> What we're trying to stop is things like "Counter * Counter" for now,
> because the obvious implementation(*) of Counter.__mul__ would do a
> strange thing with that, where a quite different thing is plausibly
> wanted (and may - or may not - be added later - but, due to backward
> compatibility, cannot be added later if the initial implementation
> does the strange thing).
>
> Rejecting a Sized argument for now would stop that.  Piling on
> additional tests could stop other things "early", but every test added
> slows the normal case (the argument is fine).
>
> In the case of an Iterable `arg` that's not Sized, it seems highly
> unlikely that arg.__mul__ or arg.__rmul__ exist, so the obvious
> implementation would blow up later without bothering to check in
> advance:
>
>     >>> x = (i for i in range(10))
>     >>> 3 * x
>     Traceback (most recent call last):
>         ...
>     TypeError: unsupported operand type(s) for *: 'int' and 'generator'
>
>
> (*) The obvious implementation:
>
>     def __mul__(self, other):
>        if isinstance(other, Sized):
>            raise TypeError("cannot multiply Counter by Sized type %s"
> % type(other))
>         result = Counter()
>         for k, v in self.items():
>             result[k] = v * other
>         return result
> _______________________________________________
> 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/
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180418/e203b9ea/attachment-0001.html>


More information about the Python-ideas mailing list