Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Counter() support non-int #3438

Open
manu3618 opened this issue Nov 4, 2019 · 13 comments · May be fixed by #11632 or #12344
Open

Counter() support non-int #3438

manu3618 opened this issue Nov 4, 2019 · 13 comments · May be fixed by #11632 or #12344
Assignees
Labels
stubs: false positive Type checkers report false errors

Comments

@manu3618
Copy link

manu3618 commented Nov 4, 2019

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
Copy link
Collaborator

srittau commented Nov 4, 2019

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
Copy link
Collaborator

srittau commented Nov 4, 2019

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 srittau added size-medium stubs: false positive Type checkers report false errors labels Nov 4, 2019
@JelleZijlstra
Copy link
Member

Some previous discussion in python/mypy#4032

@Jongy
Copy link

Jongy commented Sep 1, 2020

Hi, I arrived here from python/mypy#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 python/mypy#4032 (comment))

@JukkaL
Copy link
Contributor

JukkaL commented Sep 4, 2020

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
Copy link
Collaborator

srittau commented Sep 4, 2020

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

@qwarkys
Copy link

qwarkys commented May 31, 2022

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
Copy link
Member

AlexWaygood commented May 31, 2022

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
Copy link

qwarkys commented May 31, 2022

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 srittau added the status: deferred Issue or PR deferred until some precondition is fixed label Sep 19, 2022
@srittau
Copy link
Collaborator

srittau commented Sep 19, 2022

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

@srittau
Copy link
Collaborator

srittau commented Feb 14, 2024

See #11422 for the type var generics feature tracker.

@srittau
Copy link
Collaborator

srittau commented Mar 20, 2024

Type var defaults are now available.

@srittau srittau removed the status: deferred Issue or PR deferred until some precondition is fixed label Mar 20, 2024
@srittau srittau self-assigned this Mar 20, 2024
srittau added a commit to srittau/typeshed that referenced this issue Mar 20, 2024
@srittau srittau linked a pull request Mar 20, 2024 that will close this issue
@max-muoto max-muoto linked a pull request Jul 16, 2024 that will close this issue
@max-muoto
Copy link
Contributor

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stubs: false positive Type checkers report false errors
Projects
None yet
8 participants