Efficient way to check if dictionary key exists and has value

Viewed 2434

Let's say there's a dictionary that looks like this:

d = {"key1": "value1", "key2": {"key3": "value3"}}

The dictionary may or may not contain key2, also key2 might be empty. So in order To get value3, I would need to check if key2 and its value exist with non-null values, same applies for key3.

Now the obvious stupid solution would be like this:

if 'key2' in d:
    if d['key2']:
        if 'key3' in d['key2']:
            value = d['key2']['key3']

Now, I wonder if there's a simpler solution so I don't have to write 3 ifs in a row.

2 Answers

You can use .get() with default value:

val = d.get("key2", {}).get("key3", None)  # <-- you can put something else instead of `None`, this value will return if key2 or key3 doesn't exist

For example:

d = {"key1": "value1", "key2": {"key3": "value3"}}

val = d.get("key2", {}).get("key3", None)

if not val is None:
    print(val)
else:
    print("Not found.")

One approach would be to expect the failure and catch it:

try:
    value = d['key2']['key3']
except (KeyError, TypeError):
    pass

(don't call your variable the name of the type, it's a bad practice, I've renamed it d)

The KeyError catches a missing key, the TypeError catches trying to index something that's not a dict.

If you expect this failure to be very common, this may not be ideal, as there's a bit of overhead for a try .. except block.

In that case you're stuck with what you have, although I'd write it as:

if 'key2' in d and d['key2'] and 'key3' in d['key2']:
    value = d['key2']['key3']

Or perhaps a bit more clearly:

if 'key2' in d and isinstance(d['key2'], dict) and 'key3' in d['key2']:
    value = d['key2']['key3']

If you're about to assign something else to value in the else part (like None), you could also consider:

value = d['key2']['key3'] if 'key2' in d and d['key2'] and 'key3' in d['key2'] else None
    
Related