typeshed icon indicating copy to clipboard operation
typeshed copied to clipboard

Counter() support non-int

Open manu3618 opened this issue 6 years ago • 13 comments

I'm quite surprised because I can use a Counter to store non-integer values. Is it a feature or just permissiveness in implementation?

E.g. the following code works (tested on python 3.7.4):

from collections import Counter

spam = Counter()
spam["egg"] += 3.4

manu3618 avatar Nov 04 '19 17:11 manu3618

While the Counter implementation accepts non-int values, I would be careful to use them. Nothing in the documentation suggests that non-int values are okay to use, and Counter could break in subtle ways now or in the future. As such I believe it is safer not to change the annotations in typeshed to be more permissive.

srittau avatar Nov 04 '19 18:11 srittau

Actually I overlooked the big box which says:

Counters were primarily designed to work with positive integers to represent running counts; however, care was taken to not unnecessarily preclude use cases needing other types or negative values. To help with those use cases, this section documents the minimum range and type restrictions. [...]

PRs to improve Counter welcome!

srittau avatar Nov 04 '19 18:11 srittau

Some previous discussion in python/mypy#4032

JelleZijlstra avatar Nov 04 '19 18:11 JelleZijlstra

Hi, I arrived here from https://github.com/python/mypy/issues/4032. I'm trying to use Counter with floats, and mypy gives me Incompatible types in assignment since it expects ints.

Are we in a discussion about whether the standard library's Counter was ever meant to be used with non-integers (the answer is not obvious to me) or we need to update Counter in collections.pyi to support non-integers (for example, like suggested https://github.com/python/mypy/issues/4032#issuecomment-333158323)

Jongy avatar Sep 01 '20 10:09 Jongy

Note that changes to Counter typing may break existing annotations. There will likely be a tradeoff between allowing more flexibility and retaining compatibility with existing annotations.

JukkaL avatar Sep 04 '20 08:09 JukkaL

This is a good example, where defaults for type vars (python/typing#307) would come in handy.

srittau avatar Sep 04 '20 09:09 srittau

Have there been any changes in this or any suggested work-arounds? I'm using Counter with floats and need Counter.total(). Something like defaultdict won't help: "defaultdict[str, float]" has no attribute "total"

qwarkys avatar May 31 '22 14:05 qwarkys

Have there been any changes in this or any suggested work-arounds?

Feel free to file a PR to see what mypy_primer says in our CI, but as Jukka says, I think it's likely that changing the stubs for Counter at this point would break a lot of existing type annotations. So there's probably not much we can do here.

I'm using Counter with floats and need Counter.total(). Something like defaultdict won't help: "defaultdict[str, float]" has no attribute "total"

You could just use Counter anyway, and type: ignore the errors away. But the runtime implementation for Counter.total() is actually extremely simple, so another option would be to just subclass defaultdict and add your own total() method:

class DefaultDictWithTotal(defaultdict):
    def total(self) -> float:
        'Sum of the counts'
        return sum(self.values())

AlexWaygood avatar May 31 '22 14:05 AlexWaygood

Thanks.

FWIW, in order to pass mypy --strict I had to add 2 arguments:

class DefaultDictWithTotal(defaultdict[Any, Any]):
    def total(self) -> float:
        'Sum of the counts'
        return sum(self.values())

and then to use it: whatever = DefaultDictWithTotal(float)

qwarkys avatar May 31 '22 19:05 qwarkys

I marked this as deferred for now, pending the implementation of type var defaults.

srittau avatar Sep 19 '22 13:09 srittau

See #11422 for the type var generics feature tracker.

srittau avatar Feb 14 '24 14:02 srittau

Type var defaults are now available.

srittau avatar Mar 20 '24 03:03 srittau