I have a deep nested dict (decoded from json, from the instagram api). My initial code was like this:
caption = post['caption']['text']
But that would throw a NoneType or KeyError error if the 'caption' key or the 'text' key doesn't exist.
So I came up with this:
caption = post.get('caption', {}).get("text")
Which works, but I'm not sure about the style of it. For instance, if I apply this technique to one of the deeper nested attributes I'm trying to retrieve, it looks pretty ugly:
image_url = post.get('images',{}).get('standard_resolution',{}).get('url')
Is there a better, more pythonic, way to write this? My goal is to retrieve the data, if it's there, but not to hold up execution if it's not there.
Thanks!
The most Pythonic way would be simply to catch the KeyError
:
try:
caption = post['caption']['text']
except KeyError:
caption = None
This is simple, obvious, and immediately understandable to a Python programmer.
How do you feel about something like this
if 'caption' in post:
caption = post['caption']['text']
But it also starts to break down
if 'images' in post and 'standard_resolution' in post['images']:
image_url = post['images']['standard_resolution']['url']
So I think the most Pythonic way is to just ask for forgiveness and not permission
try:
image_url = post['images']['standard_resolution']['url']
except KeyError:
image_url = None
Python 3.4 and newer versions contains a contextlib context manager suppress
, which is for exactly this kind of thing. Suppressing specific errors when you know in advance they may happen and your code can handle it.
from contextlib import suppress
sample = {'foo': 'bar'}
with suppress(KeyError):
print(sample['baz'])
Will prevent the KeyError
from being raised.
So for accessing getting a deeply nested dictionary value, you can use suppress
like this.
value = None
with suppress(KeyError):
value = data['deeply']['nested']['dictionary']['key']
I'd create a custom dict subclass, and then just address that:
class SafeDict(dict):
def __getitem__(self,k):
if k in self:
return dict.__getitem__(self,k)
return None
a = SafeDict({'a':'a'})
print a['a']
>> a
print a['b']
>> None
You could either do a custom init to handle nested dicts as another instance of SafeDict ( which would allow you to pass them around ) or you could use testing (or a try/except block) to prevent KeyErrors
also, you could just make it an object class, overload __getattr__
, and then handle things with dot notation. i tend to prefer that approach ( I first saw this in the Pylons framework )
class AttributeSafeObject(object):
def __init__(self,**kwargs):
for key in kwargs:
setattr(self,key,kwargs[key])
def __getattr__(self, name):
try:
return object.__getattribute__(self,name)
except AttributeError:
return None
post = AttributeSafeObject({'a':'a'})
print post.a
>> a
print post.title
>> None
来源:https://stackoverflow.com/questions/15036763/what-is-the-pythonic-way-to-access-nested-dicts-without-nonetype-errors