Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chained, nested dict() get calls in python

Tags:

I'm interrogating a nested dictionary using the dict.get('keyword') method. Currently my syntax is...

M = cursor_object_results_of_db_query  for m in M:     X = m.get("gparents").get("parent").get("child")     for x in X:         y = x.get("key") 

However, sometimes one of the "parent" or "child" tags doesn't exist, and my script fails. I know using get() I can include a default in the case the key doesn't exist of the form...

get("parent", '') or get("parent", 'orphan')  

But if I include any Null, '', or empty I can think of, the chained .get("child") fails when called on ''.get("child") since "" has no method .get().

The way I'm solving this now is by using a bunch of sequential try-except around each .get("") call, but that seems foolish and unpython---is there a way to default return "skip" or "pass" or something that would still support chaining and fail intelligently, rather than deep-dive into keys that don't exist?

Ideally, I'd like this to be a list comprehension of the form:

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

but this is currently impossible when an absent parent causes the .get("child") call to terminate my program.

like image 852
Mittenchops Avatar asked Jan 23 '13 16:01

Mittenchops


People also ask

How do you get a value in a nested dictionary Python?

Access Values using get() Another way to access value(s) in a nested dictionary ( employees ) is to use the dict. get() method. This method returns the value for a specified key. If the specified key does not exist, the get() method returns None (preventing a KeyError ).

How do you loop through a nested dictionary in Python?

just write d. items() , it will work, by default on iterating the name of dict returns only the keys.

Can you explain the dict get () function?

get()—method comes in: it returns the value for a specified key in a dictionary. This method will only return a value if the specified key is present in the dictionary, otherwise it will return None.

How does Python handle nested dictionary?

Addition of elements to a nested Dictionary can be done in multiple ways. One way to add a dictionary in the Nested dictionary is to add values one be one, Nested_dict[dict][key] = 'value'. Another way is to add the whole dictionary in one go, Nested_dict[dict] = { 'key': 'value'}.


2 Answers

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.

like image 56
Martijn Pieters Avatar answered Oct 07 '22 14:10

Martijn Pieters


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.

like image 24
mgilson Avatar answered Oct 07 '22 16:10

mgilson