问题
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!
回答1:
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.
回答2:
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
回答3:
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']
回答4:
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