views:

334

answers:

5

Suppose I've got two dicts in Python:

mydict = { 'a': 0 }

defaults = {
    'a': 5,
    'b': 10,
    'c': 15
}

I want to be able to expand mydict using the default values from defaults, such that 'a' remains the same but 'b' and 'c' are filled in. I know about dict.setdefault() and dict.update(), but each only do half of what I want - with dict.setdefault(), I have to loop over each variable in defaults; but with dict.update(), defaults will blow away any pre-existing values in mydict.

Is there some functionality I'm not finding built into Python that can do this? And if not, is there a more Pythonic way of writing a loop to repeatedly call dict.setdefaults() than this:

for key in defaults.keys():
    mydict.setdefault(key, defaults[key])

Context: I'm writing up some data in Python that controls how to parse an XML tree. There's a dict for each node (i.e., how to process each node), and I'd rather the data I write up be sparse, but filled in with defaults. The example code is just an example... real code has many more key/value pairs in the default dict.

(I realize this whole question is but a minor quibble, but it's been bothering me, so I was wondering if there was a better way to do this that I am not aware of.)

A: 
defaults.update(mydict)
SilentGhost
Then how will I be able to apply my defaults to another node?
Daniel Lew
keep a copy of original somewhere.
SilentGhost
You would have to make a deepcopy of defaults first, then...
Stephan202
defaults IS the original. :P
Daniel Lew
well after you update it it won't be. and you might as well add this new info to the question.
SilentGhost
+7  A: 

Couldnt you make mydict be a copy of default, That way, mydict would have all the correct values to start with?

mydict = default.copy()
Shane C. Mason
Yeah, this is what I usually do too..
Marcus Lindblom
I thought about this, but wouldn't copying the entire defaults be pretty inefficient (especially mydict has all values set to begin with)? You'd be creating a new dict, copying all the values out of another one, then rewriting the whole dict again in that case.
Daniel Lew
@Daniel Lew: the values won't be copied, just references to them. The new dict will have new references to the same values in memory.
nosklo
You can test it for speed - it is certainly less code and more elegant in that it is immediately clear what you are trying to do. I think that it might be faster than your method if for no other reason than it is all happening inside the interpreter and is likely fairly optimized.
Shane C. Mason
+3  A: 

If you don't mind creating a new dictionary in the process, this will do the trick:

newdict = dict(defaults)
newdict.update(mydict)

Now newdict contains what you need.

DzinX
+2  A: 

You can do this the same way Python's collections.DefaultDict works:

class MultiDefaultDict(dict):
    def __init__(self, defaults, **kwargs):
        self.defaults = defaults
        self.update(kwargs)
    def __missing__(self, key):
        return self.defaults[key]

>>> mydict2 = MultiDefaultDict(defaults, a=0)
>>> mydict2['a']
0
>>> mydict2['b']
10
>>> mydict2
{'a': 0}

The other solutions posted so far duplicate all the default values; this one shares them, as requested. You may or may not want to override other dict methods like __contains__(), __iter__(), items(), keys(), values() -- this class as defined here iterates over the non-default items only.

Darius Bacon
A: 

Personally I like to append the dictionary object. It works mostly like a dictionary except that you have to create the object first.

class d_dict(dict):
    'Dictionary object with easy defaults.'
    def __init__(self,defaults={}):
        self.setdefault(defaults)           
    def setdefault(self,defaults):
        for key, value in defaults.iteritems():
            if not key in self:
                dict.__setitem__(self,key,value)

This provides the exact same functionality as the dict type except that it overrides the setsefault() method and will take a dictionary containing one or more items. You can set the defaults at creation.

This is just a personal preference. As I understand all that dict.setdefault() does is set the items which haven't been set yet. So probably the simplest in place option is:

new_dict = default_dict.copy()
new_dict.update({'a':0})

However, if you do this more than once you might make a function out of this. At this point it may just be easier to use a custom dict object, rather than constantly adding defaults to your dictionaries.