Safe method to get value of nested dictionary
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Puzzle Game Looping
--
Chapters
00:00 Safe Method To Get Value Of Nested Dictionary
00:20 Accepted Answer Score 574
01:49 Answer 2 Score 82
01:59 Answer 3 Score 18
02:12 Answer 4 Score 92
02:38 Thank you
--
Full question
https://stackoverflow.com/questions/2583...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #dictionary #methods #except
#avk47
ACCEPTED ANSWER
Score 574
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 value
example_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')
ANSWER 2
Score 92
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
ANSWER 3
Score 82
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)
ANSWER 4
Score 18
Building up on Yoav's answer, an even safer approach:
def deep_get(dictionary, *keys):
return reduce(lambda d, key: d.get(key, None) if isinstance(d, dict) else None, keys, dictionary)