Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with name clash collections.Counter and typing.Counter?

The name Counter is defined both in collections (as a class) and in typing (as a generic type name). Unfortunately, they are slightly different. What is the recommended way of dealing with this?

Similarities and differences:

  1. After from collections import Counter,

    1. you can call the constructor Counter("foo") to create a fresh Counter object;
    2. you can verify that it is subclass of dict: issubclass(Counter, dict) returns True;
    3. you cannot use it to declare a specific variant of Counter, e.g. cnt: Counter[str] = Counter("foo") raises TypeError: 'type' object is not subscriptable (the type hint fails)
  2. After from typing import Counter,

    1. you can call the constructor Counter("foo") to create a fresh Counter object (actually, somewhat to my surprise);
    2. you cannot use it to verify that it is subclass of dict: issubclass(Counter, dict) raises TypeError: issubclass() arg 1 must be a class;
    3. you can declare a specific variant of Counter, e.g. cnt: Counter[str] = Counter("foo").

In many cases 1.1 and 2.1 are good enough, so the choice of import doesn't matter. But it seems you cannot have both 1.3 and 2.2 work with a single import. Of the latter two, the type hint is more important than the subclass check. If you want to write type hints, then from typing import Counter suffices. Though, I would find it clearer (and more in line with what is needed for some other types) if you write

from collections import Counter  # to indicate that you want the implementation
from typing import Counter  # to indicate that you want to write type hints

(Note that the order matters.)

What if you want to have it all? These are the options that I see:

  1. Do
from collections import Counter
import typing

and use typing.Counter to achieve 1.3. Not nice, too wordy.

  1. Do
import collections
from typing import Counter

and use collections.Counter to achieve 2.2 (if needed; I needed it in teaching).

  1. Do
from collections import Counter as counter
from typing import Counter

and use counter to achieve 2.2.

  1. Do
from collections import Counter
from typing import Counter as Bag  # or Multiset

and use Bag (or Multiset) in type hints. (But this is bound to be confusing.)

  1. Do (as suggested in a comment)
import collections as co  # to access the class
from typing import Counter  # to access constructor and provide type hints

and use

  1. either co.Counter or Counter as the constructor
  2. use co.Counter as the class, e.g. issubclass(co.Counter, dict)
  3. use Counter in type hints, e.g. cnt: Counter[str]

Is it then also to be recommended to do

from typing import Deque

and use Deque as constructor, rather than co.deque? (I'd think/hope not.)

For other types (such as defaultdict and deque) this does not seem to be an issue:

from collections import defaultdict, deque
from typing import DefaultDict, Deque

gives you all.

Am I overlooking something?

like image 617
wstomv Avatar asked May 01 '20 21:05

wstomv


People also ask

What is Collections Counter?

class collections. Counter ([iterable-or-mapping]) A Counter is a dict subclass for counting hashable objects. It is a collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts.

What does Counter () do in Python?

Counter is a subclass of dict that's specially designed for counting hashable objects in Python. It's a dictionary that stores objects as keys and counts as values. To count with Counter , you typically provide a sequence or iterable of hashable objects as an argument to the class's constructor.

How do I extract a value from a Counter in Python?

Accessing Elements in Python Counter To get the list of elements in the counter we can use the elements() method. It returns an iterator object for the values in the Counter.

Can you use Counter on a list?

A counter can be used on a string, list, dictionary, and tuple.


1 Answers

Starting with Python 3.9 you can do:

from collections import Counter

c: Counter[str] = Counter()

See: https://docs.python.org/3/library/typing.html#typing.Counter

Deprecated since version 3.9: collections.Counter now supports []. See PEP 585 and Generic Alias Type.

like image 55
Jean Monet Avatar answered Oct 16 '22 11:10

Jean Monet