How to build a simple “deep_get” function for nested dictionaries in Python
Accessing data from nested dictionaries in python can be kinda messy. Normally you can:
- Use a bunch of
if
clauses; - Set a sequence of
get
methods, returning an empty dictionary in each of the default values.
d = {
"obj_a": { "field_x": 1, "field_y": 2},
"obj_b": {
"obj_nested": {"field_z": 3}
}
}
# to get the value of field_z using if approach
field_z = None
if "obj_b" in d and \
"obj_nested" in d["obj_b"] and \
"field_z" in d["obj_b"]["obj_nested"]:
field_z = d["obj_b"]["obj_nested"].get("field_z")
# to get the value of field_z using sequence get methods
field_z = d.get("obj_b", dict()).get("obj_nested", dict()).get("field_z")
In both cases we end up with a code that is hard to read (imagine a script with a higher number of cases like this) and hard to maintain a well (debuging something like this is terrible).
While working with some very complex yaml configurations file, I came up with a simple solution that solve the problems above and works perfecly:
from functools import reduce
def deep_get(dictionary, keys, default=None, target_type=None):
value = reduce(
lambda d, key: d.get(key, default) if isinstance(d, dict) else default,
keys.split("."),
dictionary
)
return value if not target_type else target_type(value)
It receives 2 required parameters, the dictionary itself and a string representing the key and inner keys where the desired value is contained (separated by .
).
It can also receive 2 optional parameters, a default value (just like the actual get
function of the dictionary structure) to be returned and a type class for the value to be cast to.
Since I based it in the dictionaryget
function, it also does not raise an Exception if any of the keys does not exist, it just returns the set default
value (or None
).
Lets see its usage with the same dictionary as in the example above:
d = {
"obj_a": { "field_x": 1, "field_y": 2},
"obj_b": {
"obj_nested": {"field_z": 3}
}
}
field_z = deep_get(d, "obj_b.obj_nested.field_z")
See? A LOT cleaner. Solutions like this can help a lot to maintain the code in the near future and for new team members to understand the whole logic involved as well.
The snippet used in this example can also be found here.
Feel free to access my other repositories, I post a lot of snippets and personal projects that could help you!
Thanks for reading :)