<home> <personal> <hacks> <links> <quotes> <details> <summaries>

~mwh/hacks: <bytecodehacks> <PPY> <xmms-py> <pyicqlib> <pyrepl>

setdefault

One of the new features in Python 2.0 is the setdefault method for dictionaries. This short piece is an explanation of why it's handy and when you might want to use it.

It does a natural enough job, but there's no good way to describe this job in English. Indeed, I suspect that the lack of a natural name for this method is all that has kept it out of Python before now.

Have you ever found yourself writing code like this:

class Class:
    def __init__(self):
        self.data = {}
    def add_value(self,key,value):
        if self.data.has_key(key):
           self.data[key].append(value)
        else:
           self.data[key] = [value]

or this:

class Class:
    def __init__(self):
        self.data = {}
    def add_value(self,key,value):
        try:
           self.data[key].append(value)
        except KeyError:
           self.data[key] = [value]

They both do the same thing, and neither really feels satisfactory. The fact that you end up looking up key twice in the dictionary in some circumstances seems particularly offensive.

Now the following code almost works:

class Class:
    def __init__(self):
        self.data = {}
    def add_value(self,key,value):
        self.data.get(key,[]).append(value)

The problem is that if the key is not present in the dictionary, the list that's passed as get's second parameter doesn't get added to the dictionary. So the above code doesn't actually work at all.

Basically the difference between get and setdefault is that if you use the latter in place of the former above:

class Class:
    def __init__(self):
        self.data = {}
    def add_value(self,key,value):
        self.data.setdefault(key,[]).append(value)

then the code works. A better (in some sense) name than setdefault would be get_and_maybe_setdefault, but I suspect you can see why that wasn't chosen (and even at 24 characters, it still doesn't really explain what the method does).

So, to be precise, setdefault is a method of dictionaries that takes one mandatory and one optional argument (which defaults to None). If called as

d.setdefault(k,v)

then if k is present as a key in d then d[k] is returned. If not, then d[k] is set to v and this value is then returned.

setdefault cannot replace all uses of code of the form above, because the default value has to be evaluated before the method can be called, and this sometimes can defeat the entire point of the code! E.g. a moderately common idiom in my own code is to cache the results of some expensive calculation as in

_widgets = {}

def get_widget(type):
    try:
        return _widgets[type]
    except:
        val = _expensive_call_to_make_widget(type)
        _widgets[type] = val
        return val

setdefault can clean this as far as

_widgets = {}

def get_widget(type):
    try:
        return _widgets[type]
    except:
        return _widgets.setdefault(type, \
            _expensive_call_to_make_widget(type)

but this is not nearly as good as what was acheived above. Them's the breaks.

<home> <personal> <hacks> <links> <quotes> <details> <summaries>

Valid XHTML 1.0! Valid CSS! Last updated: $Date: 2000/10/22 13:03:16 $. Comments to mwh@python.net.
Best viewed with any browser. Except netscape 4 with javascript on...