用Python列表推导过滤JSON响应

2 投票
1 回答
11370 浏览
提问于 2025-04-28 11:18

给定一个包含很多键(属性?)的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'}]

撰写回答