[Python-ideas] Fwd: collections.Counter should implement fromkeys

Steven D'Aprano steve at pearwood.info
Fri Jun 29 22:37:33 EDT 2018

On Fri, Jun 29, 2018 at 05:32:54PM -0700, Abe Dillon wrote:

> Sure, but in Hettinger's own words 
> <https://www.youtube.com/watch?v=HTLu2DFOdTg&t=24m46s> "whenever you have a 
> constructor war, everyone should get their wish". People that want a 
> counting constructor have that,
> people that want the ability to initialize values don't have that.

*scratches head*

I can initalise a Counter just fine.

py> Counter({'a': 0, 'b': 0, 'ab': 2})
Counter({'ab': 2, 'a': 0, 'b': 0})

The supported API for setting initial values of a counter is to either 
count the supplied keys:

    Counter(['a', 'b', 'ab'])

or supply initial counts in a dict:

    Counter({'a': 0, 'b': 0, 'ab': 2})

In the case where all the inital counts are zero, the obvious API is to 
call the dict fromkeys method:

    Counter(dict.fromkeys(['a', 'b', 'ab'], 0))

So what you're really asking for is a convenience method to bypass the 
need to create a temporary dict first:

    Counter.fromkeys(['a', 'b', 'ab'], 0)

Presumably the initial value will default to 0 rather than None, and 
take any integer value.

I'm sympathetic to the idea of this as a convenience, but I don't think 
its an obvious feature to have. Tim's point about duplicate keys is 
valid. Should it raise an exception, silently swallow duplicates, or 
count them?

The dict constructors, both the standard dict() and dict.fromkeys(), 
silently swallow duplicates. As they should. But Counter() does not, 
and should not.

There's a discrepency if Counter() doesn't and Counter.fromkeys() does, 
and it requires a value judgement to decide whether that discrepency is 
sufficiently unimportant.

> Technically, there is no constructor for counting by X, but if enough 
> people really wanted that, I suppose a third constructor would be in order.

How about a fourth constructor? A fifth? A fiftith? How many 
constructors is too many before the class becomes unwieldy?

Not every way you might count with a counter needs to be a constructor 
method. You can always just count:

c = Counter()
for key in keys:
    c[key] += X

I think you make a *reasonable* case for Counter.fromkeys to silently 
ignore duplicates, as a convenience method for 

    Counter(dict.fromkeys(keys, 0)

but its not (in my opinion) a *compelling* argument. I think it comes 
down to the taste of the designer.

You can always subclass it. Or even monkey-patch it.

py> def fromkeys(cls, seq, value=0):
...     c = cls()
...     for key in seq:
...             c[key] = value
...     return c
py> from collections import Counter
py> Counter.fromkeys = classmethod(fromkeys)
py> Counter.fromkeys(['a', 'b', 'ab', 'a', 'b', 'c'])
Counter({'a': 0, 'ab': 0, 'b': 0, 'c': 0})

(Subclassing is safer :-)


More information about the Python-ideas mailing list