P
Paul Rubin
El Pitonero said:What about no name at all for the scalar case:
a['hello'] += 1
a['bye'] -= 2
I like this despite the minor surprise that it works even when
a['hello'] is uninitialized.
El Pitonero said:What about no name at all for the scalar case:
a['hello'] += 1
a['bye'] -= 2
methods:Raymond said:I would like to get everyone's thoughts on two new dictionary
PROBLEMS BEING SOLVED
---------------------
The readability issues with the existing constructs are:
* They are awkward to teach, create, read, and review.
* Their wording tends to hide the real meaning (accumulation).
* The meaning of setdefault() 's method name is not self-evident.
The performance issues with the existing constructs are:
[MANY]
the performance improvement matches the readability
improvement.
Agreed.
ISSUES
The appendlist() method is not as versatile as setdefault() which can be used
with other object types (perhaps for creating dictionaries of dictionaries).
However, most uses I've seen are with lists.
I don't see a "big problem" ;-)Bengt said:How about an efficient duck-typing value-incrementer to replace both? E.g. functionally like:I would like to get everyone's thoughts on two new dictionary methods:
def count(self, value, qty=1):
try:
self[key] += qty
except KeyError:
self[key] = qty
def appendlist(self, key, *values):
try:
self[key].extend(values)
except KeyError:
self[key] = list(values)
... def valadd(self, key, incr=1):class xdict(dict):
... try: self[key] = self[key] + type(self[key])(incr)
... except KeyError: self[key] = incr
A big problem with this is that there are reasonable use cases for both
d.count(key, <some integer>)
and
d.appendlist(key, <some integer>)
Word counting is an obvious use for the first. Consolidating a list of key, value pairs where the
values are ints requires the second.
Combining count() and appendlist() into one function eliminates the second possibility.
101>>> xd.valadd('x', 100)
>>> xd['x']
[0, 1, 2, 3, 4, 5]>>> xd.valadd('y', range(3,6))
>>> xd['y']
John said:methods:
+1 for each.
+1 to EACH of the above 3 points.
A question directed to the folk who had to look up "tally" in the
dictionary: Which dictionary includes "setdefault", "updateBy", etc?
IMO Raymond's Zen concerns
are the ones to think about first, and then efficiency, which was one of the motivators
in the first place ;-)
Raymond Hettinger said:I find the disassembly (presented in the first post) to be telling.
The compiler has done a great job and there is no fluff -- all of
those steps have been specified by the programmer and he/she must at
some level be aware of every one of them (pre-instantiation,
multiple method lookups and calls, multiple dictionary accesses, etc).
Reinhold said:John Machin wrote:
Are you kidding? If you know what "set" and "default" means, you will be
able to guess what "setdefault" means. Same goes for updateBy.
"Raymond Hettinger said:The rationale is to replace the awkward and slow existing idioms for
dictionary
based accumulation:
d[key] = d.get(key, 0) + qty
d.setdefault(key, []).extend(values)
In simplest form, those two statements would now be coded more readably as:
d.count(key)
d.appendlist(key, value)
In their multi-value forms, they would now be coded as:
d.count(key, qty)
d.appendlist(key, *values)
The error messages returned by the new methods are the same as those returned
by
the existing idioms.
The get() method would continue to exist because it is useful for
applications
other than accumulation.
The setdefault() method would continue to exist but would likely not make it
into Py3.0.
"Raymond Hettinger said:Also, in all of my code base, I've not run across a single opportunity to use
something like unionset().
Raymond said:I would like to get everyone's thoughts on two new dictionary methods:
def count(self, value, qty=1):
try:
self[key] += qty
except KeyError:
self[key] = qty
def appendlist(self, key, *values):
try:
self[key].extend(values)
except KeyError:
self[key] = list(values)
-1 form me.
I'm not very glad with both of them ( not a naming issue ) because i
think that the dict type should offer only methods that apply to each
dict whatever it contains. count() specializes to dict values that are
addable and appendlist to those that are extendable. Why not
subtractable, dividable or right-shiftable? Because of majority
approval? I'm mot a speed fetishist and destroying the clarity of a
very fundamental data structure for speedup rather arbitrary
accumulations seems to be a bad idea. I would move this stuff in a
subclass.
Regards Kay
I'm not very glad with both of them ( not a naming issue ) because i
think that the dict type should offer only methods that apply to each
dict whatever it contains. count() specializes to dict values that are
addable and appendlist to those that are extendable. Why not
subtractable, dividable or right-shiftable? Because of majority
approval? I'm mot a speed fetishist and destroying the clarity of a
very fundamental data structure for speedup rather arbitrary
accumulations seems to be a bad idea. I would move this stuff in a
subclass.
Regards Kay
Raymond said:I would like to get everyone's thoughts on two new dictionary methods:
def count(self, value, qty=1):
try:
self[key] += qty
except KeyError:
self[key] = qty
def appendlist(self, key, *values):
try:
self[key].extend(values)
except KeyError:
self[key] = list(values)
Michael said:Raymond said:I would like to get everyone's thoughts on two new dictionary methods:
def count(self, value, qty=1):
try:
self[key] += qty
except KeyError:
self[key] = qty
def appendlist(self, key, *values):
try:
self[key].extend(values)
except KeyError:
self[key] = list(values)
These undoubtedly address common cases, which are unsatisfactory when spelled
using setdefault. However...
Use of these methods implicitly specializes the dictionary. The methods are
more-or-less mutually exclusive i.e., it would be at least strange to use count
and appendlist on the same dictionary. Moreover, on many dictionary instances,
the methods would fail or produce meaningless results.
This seems to be at odds with the other methods of built-in container types
which can be meaningfully applied, no matter what the types of the contents.
(There may be exceptions, but I can't think of any at the moment)
Does anyone else think this is a problem?
Michael
Raymond Hettinger said:The rationale is to replace the awkward and slow existing idioms for dictionary
based accumulation:
d[key] = d.get(key, 0) + qty
d.setdefault(key, []).extend(values)
In simplest form, those two statements would now be coded more readably as:
d.count(key)
d.appendlist(key, value)
approved, it will clearly be anGeorge said:+1 on this. The new suggested operations are meaningful for a subset of all valid dicts, so they
should not be part of the base dict API. If any version of this is
justification for applying it inapplication of the "practicality beats purity" zen rule, and the
far I'm not convinced though.this case instead of subclassing should better be pretty strong; so
Indeed not too readable. The try..except version is better but is tooAlexander said:Raymond Hettinger said:The rationale is to replace the awkward and slow existing idioms for dictionary
based accumulation:
d[key] = d.get(key, 0) + qty
d.setdefault(key, []).extend(values)
-1 from me too on these two methods because they only add "duct tape" for theYuck.
You mean giving a dictionary a default value at creation time, right?The relatively recent "improvement" of the dict constructor signature
(``dict(foo=bar,...)``) obviously makes it impossible to just extend the
constructor to ``dict(default=...)`` (or anything else for that matter) which
would seem much less ad hoc. But why not use a classmethod (e.g.
``d=dict.withdefault(0)``) then?
Too specialized IMHO. You want a dictionary with any default anyway. If youOr, for the first and most common case, just a bag type?
John Machin said:approved, it will clearly be an
justification for applying it in
far I'm not convinced though.
My background: I've been subclassing dict since this was possible, to
provide not only a count/tally method but also a DIY intern method.
Before that I just copied dictmodule.c (every release!) and diffed and
hacked about till I had a mydict module.
*any* version? Could we make you happy by having a subclass
TotallySinfulDict provided as part of the core? You don't have to use
it -- come to think of it, you don't have to use a sinful method in the
base dict. You could even avert your gaze when reading the
documentation.
The justification for having it in the core: it's in C, not in Python,
it gets implemented and maintained (a) ONCE (b) by folk like the timbot
and Raymond instead of having (a) numerous versions lashed up (b) by
you and me and a whole bunch of n00bz and b1ffz
approved, > it will clearly be an+1 on this. The new suggested operations are meaningful for a subset of all
valid dicts, so they
should not be part of the base dict API. If any version of this is
application of the "practicality beats purity" zen rule, and the
justification for applying it in
this case instead of subclassing should better be pretty strong; so far I'm
not convinced though.
George
Or similarly, something like the 'reversed' view of a sequence:Kay said:Maybe also the subclassing idea I introduced falls for short for the
same reasons. Adding an accumulator to a dict should be implemented as
a *decorator* pattern in the GoF meaning of the word i.e. adding an
interface to some object at runtime that provides special facilities.
Usage:
False
This could be generalized to other fundamental data structures as well.
Regards Kay
>>> my_tally = accumulator({}, 0, operator.add) or
>>> my_dict_of_lists = accumulator({}, [], list.append) or
>>> my_dict_of_sets = accumulator({}, set(), set.add)
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.