python / typeshed

Collection of library stubs for Python, with static types
Other
4.35k stars 1.74k forks source link

Counter() support non-int #3438

Open manu3618 opened 4 years ago

manu3618 commented 4 years ago

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
srittau commented 4 years ago

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 commented 4 years ago

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!

JelleZijlstra commented 4 years ago

Some previous discussion in python/mypy#4032

Jongy commented 4 years ago

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)

JukkaL commented 4 years ago

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.

srittau commented 4 years ago

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

qwarkys commented 2 years ago

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"

AlexWaygood commented 2 years ago

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())
qwarkys commented 2 years ago

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)

srittau commented 2 years ago

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

srittau commented 8 months ago

See #11422 for the type var generics feature tracker.

srittau commented 7 months ago

Type var defaults are now available.

max-muoto commented 2 months ago

@srittau I also opened up a PR for this, that I think covers some of the additional edge cases that weren't covered, but happy to close it if you prefer to simply implement them, or if you want to take a look.