Safe method to get value of nested dictionary Safe method to get value of nested dictionary python python

Safe method to get value of nested dictionary


You could use get twice:

example_dict.get('key1', {}).get('key2')

This will return None if either key1 or key2 does not exist.

Note that this could still raise an AttributeError if example_dict['key1'] exists but is not a dict (or a dict-like object with a get method). The try..except code you posted would raise a TypeError instead if example_dict['key1'] is unsubscriptable.

Another difference is that the try...except short-circuits immediately after the first missing key. The chain of get calls does not.


If you wish to preserve the syntax, example_dict['key1']['key2'] but do not want it to ever raise KeyErrors, then you could use the Hasher recipe:

class Hasher(dict):    # https://stackoverflow.com/a/3405143/190597    def __missing__(self, key):        value = self[key] = type(self)()        return valueexample_dict = Hasher()print(example_dict['key1'])# {}print(example_dict['key1']['key2'])# {}print(type(example_dict['key1']['key2']))# <class '__main__.Hasher'>

Note that this returns an empty Hasher when a key is missing.

Since Hasher is a subclass of dict you can use a Hasher in much the same way you could use a dict. All the same methods and syntax is available, Hashers just treat missing keys differently.

You can convert a regular dict into a Hasher like this:

hasher = Hasher(example_dict)

and convert a Hasher to a regular dict just as easily:

regular_dict = dict(hasher)

Another alternative is to hide the ugliness in a helper function:

def safeget(dct, *keys):    for key in keys:        try:            dct = dct[key]        except KeyError:            return None    return dct

So the rest of your code can stay relatively readable:

safeget(example_dict, 'key1', 'key2')


You could also use python reduce:

def deep_get(dictionary, *keys):    return reduce(lambda d, key: d.get(key) if d else None, keys, dictionary)


By combining all of these answer here and small changes that I made, I think this function would be useful. its safe, quick, easily maintainable.

def deep_get(dictionary, keys, default=None):    return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)

Example :

>>> from functools import reduce>>> def deep_get(dictionary, keys, default=None):...     return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)...>>> person = {'person':{'name':{'first':'John'}}}>>> print (deep_get(person, "person.name.first"))John>>> print (deep_get(person, "person.name.lastname"))None>>> print (deep_get(person, "person.name.lastname", default="No lastname"))No lastname>>>