I have a nested dictionary object and I want to be able to retrieve values of keys with an arbitrary depth. I'm able to do this by subclassing dict
:
>>> class MyDict(dict): ... def recursive_get(self, *args, **kwargs): ... default = kwargs.get('default') ... cursor = self ... for a in args: ... if cursor is default: break ... cursor = cursor.get(a, default) ... return cursor ... >>> d = MyDict(foo={'bar': 'baz'}) >>> d {'foo': {'bar': 'baz'}} >>> d.get('foo') {'bar': 'baz'} >>> d.recursive_get('foo') {'bar': 'baz'} >>> d.recursive_get('foo', 'bar') 'baz' >>> d.recursive_get('bogus key', default='nonexistent key') 'nonexistent key'
However, I don't want to have to subclass dict
to get this behavior. Is there some built-in method that has equivalent or similar behavior? If not, are there any standard or external modules that provide this behavior?
I'm using Python 2.7 at the moment, though I would be curious to hear about 3.x solutions as well.
get()—method comes in: it returns the value for a specified key in a dictionary. This method will only return a value if the specified key is present in the dictionary, otherwise it will return None.
Creating Python Dictionary While the values can be of any data type and can repeat, keys must be of immutable type (string, number or tuple with immutable elements) and must be unique. As you can see from above, we can also create a dictionary using the built-in dict() function.
The setup is simple: the two different dictionaries - with dict() and {} - are set up with the same number of elements (x-axis). For the test, each possible combination for an update is run.
A very common pattern to do this is to use an empty dict as your default:
d.get('foo', {}).get('bar')
If you have more than a couple of keys, you could use reduce
(note that in Python 3 reduce
must be imported: from functools import reduce
) to apply the operation multiple times
reduce(lambda c, k: c.get(k, {}), ['foo', 'bar'], d)
Of course, you should consider wrapping this into a function (or a method):
def recursive_get(d, *keys): return reduce(lambda c, k: c.get(k, {}), keys, d)
@ThomasOrozco's solution is correct, but resorts to a lambda
function, which is only necessary to avoid TypeError
if an intermediary key does not exist. If this isn't a concern, you can use dict.get
directly:
from functools import reduce def get_from_dict(dataDict, mapList): """Iterate nested dictionary""" return reduce(dict.get, mapList, dataDict)
Here's a demo:
a = {'Alice': {'Car': {'Color': 'Blue'}}} path = ['Alice', 'Car', 'Color'] get_from_dict(a, path) # 'Blue'
If you wish to be more explicit than using lambda
while still avoiding TypeError
, you can wrap in a try
/ except
clause:
def get_from_dict(dataDict, mapList): """Iterate nested dictionary""" try: return reduce(dict.get, mapList, dataDict) except TypeError: return None # or some other default value
Finally, if you wish to raise KeyError
when a key does not exist at any level, use operator.getitem
or dict.__getitem__
:
from functools import reduce from operator import getitem def getitem_from_dict(dataDict, mapList): """Iterate nested dictionary""" return reduce(getitem, mapList, dataDict) # or reduce(dict.__getitem__, mapList, dataDict)
Note that []
is syntactic sugar for the __getitem__
method. So this relates precisely how you would ordinarily access a dictionary value. The operator
module just provides a more readable means of accessing this method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With