views:

189

answers:

3

The addition of collections.defaultdict in Python 2.5 greatly reduced the need for dict's setdefault method. This question is for our collective education:

  1. What is setdefault still useful for, today in Python 2.6/2.7?
  2. What popular use cases of setdefault were superseded with collections.defaultdict?
+6  A: 

You could say defaultdict is useful for settings defaults before filling the dict and setdefault is useful for setting defaults while or after filling the dict.

Probably the most common use case: Grouping items (in unsorted data, else use itertools.groupby)

# really verbose
new = {}
for (key, value) in data:
    if key in new:
        new[key].append( value )
    else:
        new[key] = [value]


# easy with setdefault
new = {}
for (key, value) in data:
    group = new.setdefault(key, []) # key might exist already
    group.append( value )


# even simpler with defaultdict 
new = defaultdict(list)
for (key, value) in data:
    new[key].append( value ) # all keys have a default already

Sometimes you want to make sure that specific keys exist after creating a dict. defaultdict doesn't work in this case, because it only creates keys on explicit access. Think you use something HTTP-ish with many headers -- some are optional, but you want defaults for them:

headers = parse_headers( msg ) # parse the message, get a dict
# now add all the optional headers
for headername, defaultvalue in optional_headers:
    headers.setdefault( headername, defaultvalue )
THC4k
Indeed, this IMHO is the chief use case for replacement by `defaultdict`. Can you give an example of what your mean in the first paragraph?
Eli Bendersky
I would certainly not do that for the last example. Why not use `headers = dict(optional_headers); headers.update(parse_headers(msg))` or even a `defaultdict` for headers before using `update`?
Muhammad Alkarouri
Muhammad Alkarouri: What you do first is copy the dict then overwrite some of the items. I do that a lot too and I guess that is actually the idiom most prefer over `setdefault`. A `defaultdict` on the other hand wouldn't work if not all the `defaultvalues` are equal (ie some are `0` and some are `[]`).
THC4k
@YHC4k, yes. That is why I used `headers = dict(optional_headers)`. For the case when the default values are not all equal. And the end result is the same as if you get the HTTP headers first then set the defaults for those you didn't get. And it is quite usable if you already have `optional_headers`. Try my given 2 step code and compare it to yours, and you'll see what I mean.
Muhammad Alkarouri
+1  A: 

Theoretically speaking, setdefault would still be handy if you sometimes want to set a default and sometimes not. In real life, I haven't come across such a use case.

However, an interesting use case comes up from the standard library (Python 2.6, _threadinglocal.py):

>>> mydata = local()
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]

I would say that using __dict__.setdefault is a pretty useful case.

Edit: As it happens, this is the only example in the standard library and it is in a comment. So may be it is not enough of a case to justify the existence of setdefault. Still, here is an explanation:

Objects store their attributes in the __dict__ attribute. As it happens, the __dict__ attribute is writeable at any time after the object creation. It is also a dictionary not a defaultdict. It is not sensible for objects in the general case to have __dict__ as a defaultdict because that would make each object having all legal identifiers as attributes. So I can't foresee any change to Python objects getting rid of __dict__.setdefault, apart from deleting it altogether if it was deemed not useful.

Muhammad Alkarouri
Could you elaborate - what makes __dict_.setdefault particularly useful?
Eli Bendersky
@Eli: I think the point is that `__dict__` is by implementation a `dict`, not a `defaultdict`.
katrielalex
katrielalex is right. I will expand the answer to a clearer explanation later.
Muhammad Alkarouri
@Eli: edit done.
Muhammad Alkarouri
Alright. I don't mind about `setdefault` staying in Python, but it's curious to see that it's now almost useless.
Eli Bendersky
@Eli: I agree. I don't think there are enough reasons for it to be introduced today if it wasn't there. But being there already, it would be difficult to argue for removing it, given all the code using it already.
Muhammad Alkarouri
+4  A: 

I commonly use setdefault for keyword argument dicts, such as in this function:

def notify(self, level, *pargs, **kwargs):
    kwargs.setdefault("persist", level >= DANGER)
    self.__defcon.set(level, **kwargs)
    try:
        kwargs.setdefault("name", self.client.player_entity().name)
    except pytibia.PlayerEntityNotFound:
        pass
    return _notify(level, *pargs, **kwargs)

It's great for tweaking arguments in wrappers around functions that take keyword arguments.

Matt Joiner