7

Currently I am using the following method, assume dictionary

data[a][b][c]

I use:

if "a" in data and "b" in data["a"] and "c" in data["a"]["b"]:
  ...

Are there any better way?

Ryan
  • 10,191
  • 23
  • 85
  • 145

4 Answers4

7

You can wrap it in a try/except block

try:
    x = data[a][b][c]
    ...do stuff in your "if" clause
except KeyError:
    ...do stuff in your "else" clause
tdelaney
  • 63,514
  • 5
  • 71
  • 101
5

I would typically use the pattern

foo = data.get("a",{}).get("b",{}).get("c",False)
if foo:
...

This requires that the nesting is always the same depth and that data["a"]["b"]["c"] is not "falsy".

colcarroll
  • 3,542
  • 15
  • 25
1

There's two solutions:

Return a default value with defaultdict

You can use a defaultdict, and intstantiate an inner level with another defaultdict. Then, if the key is missing, it'll return an empty dict without throwing errors:

from collections import defaultdict

>>> d = defaultdict(lambda: defaultdict(dict))
>>> d['a']['b']
{}

If you need more levels, you just have to chain many lambda.

Partial reference: Multiple levels of 'collection.defaultdict' in Python

Handle the missing key with a try/except

Another way is to surronding the dict access with try/except block and manage the missing key through the KeyError exception (stop the program, return a default value, etc.)

try:
    x = data[a][b][c]
    # same code of your if statement
except KeyError:
    # stuff you do in your else
Community
  • 1
  • 1
Maxime Lorant
  • 31,821
  • 17
  • 84
  • 96
1

If you need to do this multi-level lookup very often, this helper method can be useful:

def multi_get(dict_obj, *attrs, default=None):
    result = dict_obj
    for attr in attrs:
        if attr not in result:
            return default
        result = result[attr]
    return result

To use it, just call it like:

x = multi_get(data, 'a', 'b', 'c', default=False)
Martín De la Fuente
  • 5,316
  • 4
  • 23
  • 27