Chained, nested dict() get calls in python

How about using a small helper function?

def getn(d, path):
    for p in path:
        if p not in d:
            return None
        d = d[p]
    return d

and then

[getn(m, ["gparents", "parent", "child"]) for m in M]

Since these are all python dicts and you are calling the dict.get() method on them, you can use an empty dict to chain:

[m.get("gparents", {}).get("parent", {}).get("child") for m in M]

By leaving off the default for the last .get() you fall back to None. Now, if any of the intermediary keys is not found, the rest of the chain will use empty dictionaries to look things up, terminating in .get('child') returning None.


Another approach is to recognize that if the key isn't found, dict.get returns None. However, None doesn't have an attribute .get, so it will throw an AttributeError:

for m in M:
    try:
       X = m.get("gparents").get("parent").get("child")
    except AttributeError:
       continue

    for x in X:
        y = x.get("key")
        #do something with `y` probably???

Just like Martijn's answer, this doesn't guarantee that X is iterable (non-None). Although, you could fix that by making the last get in the chain default to returning an empty list:

 try:
    X = m.get("gparents").get("parent").get("child",[])
 except AttributeError:
    continue

Finally, I think that probably the best solution to this problem is to use reduce:

try:
    X = reduce(dict.__getitem__,["gparents","parent","child"],m)
except (KeyError,TypeError):
    pass
else:
    for x in X:
       #do something with x

The advantage here is that you know if any of the gets failed based on the type of exception that was raised. It's possible that a get returns the wrong type, then you get a TypeError. If the dictionary doesn't have the key however, it raises a KeyError. You can handle those separately or together. Whatever works best for your use case.