用Python列表推导过滤JSON响应
给定一个包含很多键(属性?)的json对象,比如:
[{'name': 'Bob', 'infos': {'spam': 'eggs', 'foo': 'bar'}},
{'name': 'Tom'},
{'name': 'Lisa', 'infos': {'spam': 'qux', 'foo': 'baz'}}
...]
我希望能用列表推导式来过滤掉那些entry['infos']['spam'] == 'eggs'
的条目。
如果可以的话,我更喜欢使用列表推导式,但到目前为止,我唯一的解决方案是使用多个.get()
,而且越往树的底部,放得越靠右(这样可以避免KeyError
,因为在到达那里之前就让这个表达式变成False
)。
例如,
# Will obviously fail with KeyError
[each for each in my_json if each['infos']['spam'] == 'eggs']
# Works but requires a separate / additional `.get()`, and only works
# because it is returning False before it evaluates all conditions
[each for each in my_json if each.get('infos') and each.get('infos').get('spam') == 'eggs']
# Fails as all conditions will be evaluated before running
[each for each in my_json if all([each.get('infos'), each.get('infos').get('spam') == 'eggs'])]
# Not a list comprehension, but concise... and also doesn't work
filter(lambda x: x['infos']['spam'] == 'eggs', my_json)
有没有更好的方法来过滤我的json响应?我之所以这样问,是因为有些API返回的json对象中,感兴趣的键在很深的地方……而使用像each.get('a') and each['a'].get('b') and each['a']['b'].get('c') == 'd'
这样的方式,似乎很累,只是为了验证each['a']['b']['c'] == 'd'
。
我想我可以总是使用try
except KeyError
。
mylist = []
for each in my_json:
try:
if each['infos']['spam'] == 'eggs':
mylist.append(each)
except KeyError:
pass
有没有我忽略的明显解决方案(最好是在python3的标准库中),可以消除所有有效解决方案中的冗余?
1 个回答
7
你可以为get
设置一个默认值,这样如果找不到对应的键,就会使用这个默认值。例如,你可以这样写:
[each for each in my_json if each.get('infos', {}).get('spam') == 'eggs']
第一个get('infos', {})
的意思是,如果找不到' infos'这个键,就用一个空的字典作为默认值,这样第二个get
就不会出错了。
这里是用filter
的方式:
>>> filter(lambda x: x.get('infos', {}).get('spam') == 'eggs', my_json)
[{'infos': {'foo': 'bar', 'spam': 'eggs'}, 'name': 'Bob'}]
需要注意的是,如果外层字典中有' infos'这个键,但它不是一个字典,这样的代码还是会出错。
一个更稳妥的方法是定义一个过滤函数:
>>> def wonderful_spam(x):
... try:
... return x['infos']['spam'] == 'eggs'
... except (KeyError, TypeError):
... return False
...
>>> filter(wonderful_spam, my_json)
[{'infos': {'foo': 'bar', 'spam': 'eggs'}, 'name': 'Bob'}]
>>> [x for x in my_json if wonderful_spam(x)]
[{'infos': {'foo': 'bar', 'spam': 'eggs'}, 'name': 'Bob'}]