[Tutor] setdefault()

Michael Janssen Janssen at rz.uni-frankfurt.de
Sat Feb 14 10:14:15 EST 2004

On Fri, 13 Feb 2004, Christopher Spears wrote:

> Can someone clarify what setdefault() does?  I looked
> it up in the docs, and I am still confused.

Whiles the doc's explanation "a[k] if k in a, else x (also setting it)"
is quite clear, showing some use cases might help to understand *why*
there is such a method. When not concerned about the "also setting it"
part, you can as easily use the get method:

>>> default = 1
>>> val = aDict.get("key", default)

which is nicer to type or read than catching the exception on a hard key

>>> try: val = aDict["key"]
>>> except KeyError: val = default

But why should one be concerned about "also setting it"? For the ease of
the next lookup? No, you just can go on with the get-method and it's
fine. So why is there the setdefault method? Just to spare a step when
you really want to both getting and setting a value? This would be
two till four lines of code. That few lines wouldn't be worth adding a
method into core python.

I belive existence of the setdefault method is due to a more or less
common task, that would be otherwise ugly to write. Imaging a dictionary
with lists as values:

>>> aDict = {"a": []}

Now you want to append items to the lists. It's obviously easy with an
existing list:

>>> aDict["a"].append(0)

You can even use get (this uses python's nature of references pointing
to shared objects. You need problably some time to understand this, but
it's an important feature and also the basic for my setdefault usecase
I'm going to show):

>>> aDict.get("a").append(1)

---> get retrieves the value (a list) for key "a" and 1 gets appended on
this returned lists. Since python just do references to internal objects
the returned list and the list within aDict are the same object and
"append(1)" reflect on aDict["a"] :

>>> aDict
{'a': [1, 2]}

You can't either do aDict["b"].append(1) (This would be a KeyError) nor:

>>> aDict.get("b", []).append(0)
>>> aDict
{'a': [1, 2]}

---> get("b", []) has returned a new empty list as default value.
append has appended an 0 on this list, which unfurtunately has gone away
because get hasn't "also setting it". This is where setdefault comes

>>> aDict.setdefault("b", []).append(0)
>>> aDict
{'a': [1, 2], 'b': [0]}

setdefault returns the empty list and also glueing this same list into
aDict. append works on the returned list which is a reference to the
same list within aDict.

So, when you have a dictionary of lists and want append to them, you can
do it very easy with setdefault. Say you want to analyze a webserver log
and you want to know from which ip's what pages are called. In pseudo
code it's somethind like this:

ip2pages = {}
for line in file_object:
    ip, page = function_to_analyze_line(line)
    ip2pages.setdefault(ip, []).append(page)

After that ip2pages is a dictionary mapping each ip to a list of pages.
Here's the handwoven version:

ip2pages = {}
for line in file_object:
    ip, page = function_to_analyze_line(line)
    if ip2pages.has_key(ip):
        ip2pages[ip] = [page]

The latter verson isn't much worse but setdefaulting an empty list is a
common task (at least for me since I've started to analyze our
webservers log ;-) and very useful, so it's worth to have such a method.

You can also setdefaulting dictionaries but I havn't seen this (which
doesn't say much ;-) and it looks redicolous:

>>> aDict.setdefault("c", {})["new"] = 5
>>> aDict
{'a': [1, 2], 'c': {'new': 5}, 'b': [0]}

Setdefaulting ints, tuples or strings is much more useless, because you
can't change them in place. When you work on the return value of
setdefault by changing the value, a new object is created and this won't
reflect within aDict's value:

>>> aDict.setdefault("d", 0) + 1
1   <--- look here, a new object ;-)
>>> aDict
{'a': [1, 2], 'c': {'new': 5}, 'b': [0], 'd': 0}

Here you're better of with get:

aDict_ofInts[key] = aDict_ofInts.get(key, 0) + 1


More information about the Tutor mailing list