Question

TLDR summary

I wrote a function navigateDict that does a safe navigation on a dict, similar to dict.get() but nested. It replaces code like

if 1 in data and 'i' in data[1] and 'a' in data[1]['i']:
    print data[1]['i']['a']
else:
    print "Not found"

with the roughly equivalent

found = navigateDict(data, 1, 'i', 'a')
if found is not None:
    print found
else:
    print "Not found"
  • Is anything similar to this already part of the standard library?
  • Is there a more idiomatic way to do the same thing?
    • Any response that requires typing any path component key more than once is probably a non-answer.

Additional details

The implementation is as follows:

# Allow fallback value other than None
def navigateDictEx(d, keys, fallback=None):
    for key in keys:
        if key in d:
            d = d[key]
        else:
            return fallback
    return d

def navigateDict(d, *keys):
    return navigateDictEx(d, keys)

See the summary for example usage.

Pythonic or not, this function reduces repetition in a place where redundancy is a bad idea. For example, changing one path component in the example requires up to three distinct values to be modified as one in the original example, but only one in the modified example. Given my regular tendency to err, this is a big win.

Ultimately I'm asking this: Is there something in the standard library that does this, or am I going to need to find a place for it in my project's library?

If hits are expected to dominate misses

brionius correctly points out that catching KeyError will work:

try:
    print data[1]['i']['a']
except KeyError:
    print "Not found"

This might be the way I go; it's pretty terse and cuts the repetition. However, it does reflect an assumption that there will be more hits than misses. If there's a better way of assuming the opposite I'd like to know that, also.

Was it helpful?

Solution

One way to do this is as follows:

try:
    print data[1]['i']['a']
except KeyError:
    print "Not found!"

It's in line with the spirit of duck-typing. It may or may not be as fast, as I believe handling exceptions carries a certain amount of overhead, but it's certainly "safe".

OTHER TIPS

a solution like this is cool

https://twitter.com/raymondh/status/343823801278140417

>>> from collections import defaultdict
>>> infinite_defaultdict = lambda: defaultdict(infinite_defaultdict)
>>> d = infinite_defaultdict() 
>>> d['x']['y']['z'] = 10
>>> if d['x']['y']['z']: print d['x']['y']['z'] #better reflects that misses are common

Years late to the game, but for anyone stumbling upon this, there still does not seem to be a native, fluent way to safely navigate a Python dict.

Enter RestResponse:

"RestResponse aims to be a fluent python object for interfacing with RESTful JSON APIs"

This library includes a NoneProp object that allows for safely navigating (and building) JSON data structures.

>>> import RestResponse
>>> data = RestResponse.parse({})
>>> data.property.is_none
None
>>> bool(data.property.is_none)
False
>>> isinstance(data.property.is_none, RestResponse.NoneProp)
True
>>> data.property.is_none = None
>>> isinstance(data.property.is_none, RestResponse.NoneProp)
False
>>> print data.pretty_print()
{
    "property": {
        "is_none": null
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top