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

Abe Dillon abedillon at gmail.com
Fri Jun 29 23:50:09 EDT 2018


[Steven D'Aprano]

> 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))


Yes, I've discussed this, but since my replies have been miss addressed, it
may have gotten lost.
I'll quote it below:

[Abe Dillon]

> Counter(dict.fromkeys(keys, value)) works just fine, but it feels wrong.
> I'm using the copy-constructor because I know Counter is a subclass of
> dict.
> I'm using fromkeys because I know how that class method works.
> So why does the subclass lack functionality that the superclass has?
> Because programmers wouldn't be able to wrap their heads around it?
> I don't buy it. This feels like nanny-design trumping SOLID design
> <https://en.wikipedia.org/wiki/SOLID>.


[Steven D'Aprano]

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


I'm not asking for anything all that new. Just that the existing .fromkeys
inherited from dict not be disabled.

[Steven D'Aprano]

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


Yes. I think that would make the most sense. 0 or 1. As long as it's
documented it doesn't matter to me.

[Steven D'Aprano]

> Tim's point about duplicate keys is valid. Should it raise an exception,
> silently swallow duplicates, or
> count them?


It should do exactly what dict.fromkeys does (except with a numeric
default): ignore duplicates

[Steven D'Aprano]

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


That's fine. I don't think that's confusing.

[Steven D'Aprano]

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


I think this is a little overboard on the slippery-slope, no? I'm asking
for a constructor that already exists, but was deliberately disabled.
As far as I can tell, the only people pointing out that others will
complain are playing devil's advocate.
I can't tell if there are any people that actually believe that
Counter.fromkeys should have a multiplier effect.
I wouldn't expect the campaign for the third type of constructor to get
very far. Especially if Counter multiplication gets accepted.

[Tim]

> I think the missing bit here is that there weren't any "constructor wars"
> for Counter...

15 years later you're jumping up & down about Counter.fromkeys() not being
> there, and that's why nobody much cares ;-)


I haven't been part of the conversation for 15 years, but most of the
argument against the idea (yours especially) seem to focus on the prospect
of a constructor war and imply that was the original motivation behind
actively disabling the fromkeys method in Counters. I don't mean to give
the impression that I'm fanatical about this. It really is a minor
inconvenience. It doesn't irk me nearly as much as other minor things, like
that the fact that all the functions in the heapq package begin with the
redundant word 'heap'.

[Tim]

> Raymond may have a different judgment about that, though.  I don't believe
> he reads python-ideas anymore


He actually did reply a few comments back!
I think I'm having more fun chatting with people that I deeply respect than
"jumping up and down". I'm sorry if I'm coming off as an asshole. We can
kill this thread if everyone thinks I'm wasting their time. It doesn't look
like anyone else shares my minor annoyance. Thanks for indulging me!

On Fri, Jun 29, 2018 at 9:37 PM, Steven D'Aprano <steve at pearwood.info>
wrote:

> 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 :-)
>
>
>
> --
> Steve
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180629/63dd2cfb/attachment-0001.html>


More information about the Python-ideas mailing list