Python - accessing values nested within dictionaries

I have a dictionary which contains dictionaries, which may also contain dictionaries, e.g.

dictionary = {'ID': 0001, 'Name': 'made up name', 'Transactions':
               {'Transaction Ref': 'a1', 'Transaction Details':
                  {'Bill To': 'abc', 'Ship To': 'def', 'Product': 'Widget A'
                      ...} ...} ... }

Currently I'm unpacking to get the 'Bill To' for ID 001, 'Transaction Ref' a1 as follows:

if dictionary['ID'] == 001:
    transactions = dictionary['Transactions']
        if transactions['Transaction Ref'] == 'a1':
            transaction_details = transactions['Transaction Details']
            bill_to = transaction_details['Bill To']

I can't help but think this is is a little clunky, especially the last two lines - I feel like something along the lines of the following should work:

bill_to = transactions['Transaction Details']['Bill To']

Is there a simpler approach for drilling down into nested dictionaries without having to unpack into interim variables?

Answers


bill_to = transactions['Transaction Details']['Bill To']

actually works. transactions['Transaction Details'] is an expression denoting a dict, so you can do lookup in it. For practical programs, I would prefer an OO approach to nested dicts, though. collections.namedtuple is particularly useful for quickly setting up a bunch of classes that only contain data (and no behavior of their own).

There's one caveat: in some settings, you might want to catch KeyError when doing lookups, and in this setting, that works too, it's hard to tell which dictionary lookup failed:

try:
    bill_to = transactions['Transaction Details']['Bill To']
except KeyError:
    # which of the two lookups failed?
    # we don't know unless we inspect the exception;
    # but it's easier to do the lookup and error handling in two steps

You can use something like this:

>>> def lookup(dic, key, *keys):
...     if keys:
...         return lookup(dic.get(key, {}), *keys)
...     return dic.get(key)
...
>>> d = {'a':{'b':{'c':5}}}
>>> print lookup(d, 'a', 'b', 'c')
5
>>> print lookup(d, 'a', 'c')
None

Additionally, if you don't want to define your search keys as individual parameters, you can just pass them in as a list like this:

>>> print lookup(d, *['a', 'b', 'c'])
5
>>> print lookup(d, *['a', 'c'])
None

Following is another way of accessing nested dictionaries

>>> dbo={'m':{'d':{'v':{'version':1}}}}
>>> name='m__d__v__version' # it'll refer to 'dbo['m']['d']['v']['version']', '__' is the separator
>>> version = reduce(dict.get, name.split('__'), dbo)
>>> print version
1
>>>

Here, variable 'name' refers to 'dbo['m']['d']['v']['version']', which seems much shorter and neat.

This method will not throw KeyError. If a key is not found then you'll get 'None'.

Ref.: http://code.activestate.com/recipes/475156-using-reduce-to-access-deeply-nested-dictionaries/


Need Your Help

Faster way to read fixed-width files

r substring data.table apply lapply

I work with a lot of fixed width files (i.e., no separating character) that I need to read into R. So, there is usually a definition of the column width to parse the string into variables. I can ...

How to make Twitter Bootstrap menu dropdown on hover rather than click

css drop-down-menu twitter-bootstrap

I'd like to have my Bootstrap menu automatically drop down on hover, rather than having to click the menu title. I'd also like to lose the little arrows next to the menu titles.