假设我有这个:

[
  {"name": "Tom", "age": 10},
  {"name": "Mark", "age": 5},
  {"name": "Pam", "age": 7}
]

通过搜索“Pam”作为名称,我想检索相关的字典:{name:“Pam”,年龄:7}

如何做到这一点?


当前回答

我测试了各种方法来遍历字典列表并返回键x具有特定值的字典。

结果:

速度:列表推导>生成器表达式>>常规列表迭代>>>过滤器。 所有的比例都与列表中的字典数量线性(10x列表大小-> 10x时间)。 对于大量(数千个)键,每个字典的键不会显著影响速度。请看我计算的图表:https://imgur.com/a/quQzv(方法名称见下文)。

所有测试均使用Python 3.6.4, W7x64完成。

from random import randint
from timeit import timeit


list_dicts = []
for _ in range(1000):     # number of dicts in the list
    dict_tmp = {}
    for i in range(10):   # number of keys for each dict
        dict_tmp[f"key{i}"] = randint(0,50)
    list_dicts.append( dict_tmp )



def a():
    # normal iteration over all elements
    for dict_ in list_dicts:
        if dict_["key3"] == 20:
            pass

def b():
    # use 'generator'
    for dict_ in (x for x in list_dicts if x["key3"] == 20):
        pass

def c():
    # use 'list'
    for dict_ in [x for x in list_dicts if x["key3"] == 20]:
        pass

def d():
    # use 'filter'
    for dict_ in filter(lambda x: x['key3'] == 20, list_dicts):
        pass

结果:

1.7303 # normal list iteration 
1.3849 # generator expression 
1.3158 # list comprehension 
7.7848 # filter

其他回答

你可以通过使用Python中的filter和next方法来实现这一点。

方法过滤给定序列并返回一个迭代器。 Next方法接受迭代器并返回列表中的下一个元素。

所以你可以通过,

my_dict = [
    {"name": "Tom", "age": 10},
    {"name": "Mark", "age": 5},
    {"name": "Pam", "age": 7}
]

next(filter(lambda obj: obj.get('name') == 'Pam', my_dict), None)

输出是,

{'name': 'Pam', 'age': 7}

注意:如果没有找到所搜索的名称,上述代码将返回None。

names = [{'name':'Tom', 'age': 10}, {'name': 'Mark', 'age': 5}, {'name': 'Pam', 'age': 7}]
resultlist = [d    for d in names     if d.get('name', '') == 'Pam']
first_result = resultlist[0]

这是一种方法……

你可以使用生成器表达式:

>>> dicts = [
...     { "name": "Tom", "age": 10 },
...     { "name": "Mark", "age": 5 },
...     { "name": "Pam", "age": 7 },
...     { "name": "Dick", "age": 12 }
... ]

>>> next(item for item in dicts if item["name"] == "Pam")
{'age': 7, 'name': 'Pam'}

如果你需要处理不存在的项,那么你可以做用户Matt在他的评论中建议的事情,并使用稍微不同的API提供默认值:

next((item for item in dicts if item["name"] == "Pam"), None)

并且要查找项目的索引,而不是项目本身,您可以枚举()列表:

next((i for i, item in enumerate(dicts) if item["name"] == "Pam"), None)

这里提出的大多数(如果不是全部)实现都有两个缺陷:

他们假设只传递一个键来进行搜索,而对于复杂的字典,有更多的键可能是有趣的 它们假定所有传递用于搜索的键都存在于字典中,因此当KeyError不存在时,它们不会正确处理。

更新后的命题:

def find_first_in_list(objects, **kwargs):
    return next((obj for obj in objects if
                 len(set(obj.keys()).intersection(kwargs.keys())) > 0 and
                 all([obj[k] == v for k, v in kwargs.items() if k in obj.keys()])),
                None)

也许不是最python化的,但至少更安全一点。

用法:

>>> obj1 = find_first_in_list(list_of_dict, name='Pam', age=7)
>>> obj2 = find_first_in_list(list_of_dict, name='Pam', age=27)
>>> obj3 = find_first_in_list(list_of_dict, name='Pam', address='nowhere')
>>> 
>>> print(obj1, obj2, obj3)
{"name": "Pam", "age": 7}, None, {"name": "Pam", "age": 7}

要点。

dicts=[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]

from collections import defaultdict
dicts_by_name=defaultdict(list)
for d in dicts:
    dicts_by_name[d['name']]=d

print dicts_by_name['Tom']

#output
#>>>
#{'age': 10, 'name': 'Tom'}