Josep Valls
Josep Valls

Reputation: 5560

Set default values in dictionaries when not existent

I have dictionaries for which I want to assign a value to a key if that key doesn't exist. I found myself writing this pretty often:

class Document(object):
    def __init__(properties):
        self.properties = properties
        self.properties['created']=properties.get('created',datetime.datetime.now())
        self.properties['foo']=properties.get('foo','bar')

Someone suggested this equivalent:

        self.properties = {'created',datetime.datetime.now(),'foo':'bar'}
        self.properties.update(properties)

Is there any benefit from using one or the other? I was hoping to achieve this assignment in one line but unfortunately the update() method does not return the modified dictionary. Is there a good performant one-liner alternative?

Upvotes: 1

Views: 1329

Answers (2)

rofls
rofls

Reputation: 5115

You can use a defaultdict from the collections module:

from collections import defaultdict
>>> d=defaultdict(datetime.datetime.now)
>>> d['created']
datetime.datetime(2016, 2, 4, 16, 44, 42, 767328)

You could also use setdefault, a dict method:

class Document(object):
    def __init__(self,properties):
        self.properties = properties
        self.properties.setdefault('created',datetime.datetime.now())

Using setdefault won't set the other dict values to datetime.datetime.now(), so it is likely more appropriate for you.

Upvotes: 1

ShadowRanger
ShadowRanger

Reputation: 155333

If you're able to use Python 3.5, additional unpacking generalizations makes this easy:

class Document(object):
    def __init__(self, properties):
        self.properties = {'created': datetime.datetime.now(), **properties}

If the properties dict is guaranteed to only have string keys, you could also do the following in any version of Python:

        self.properties = dict({'created': datetime.datetime.now()}, **properties)

And while it's a little ugly, for any version of Python, you can get a similar effect to the additional unpacking generalizations with:

from itertools import chain

...
        self.properties = dict(chain({'created': datetime.datetime.now()}.items(), properties.items()))

Lastly, if none of those are an option, you can at least avoid separate get and store operations, by using setdefault:

        self.properties = properties
        properties.setdefault('created', datetime.datetime.now())

setdefault acts like get, except it also stores the default value if there is no existing key. Note that like the get based version, this keeps the exact dict provided by the caller, mutating it, and remaining subject to being mutated by the caller in the future. All the other solutions avoid this. To fix that for either get or setdefault, change to:

        self.properties = properties.copy()
        self.properties.setdefault('created', datetime.datetime.now())

Upvotes: 1

Related Questions