Python在列表中查找对象

11

我有一个人员列表:

[
    {'name' : 'John', 'wins' : 10 },
    {'name' : 'Sally', 'wins' : 0 },
    {'name' : 'Fred', 'wins' : 3 },
    {'name' : 'Mary', 'wins' : 6 }
]

我正在使用一个名称列表(['Fred', 'Mary', 'Sally'])添加胜利次数。我不知道这个名字是否已经在人员列表中,如果没有,我需要插入一条新记录。目前我正在执行以下操作:

name = 'John'
person = None
pidx = None
for p in people_list:
    if p['name'] == name:
        person = p
        pidx = people_list.index(p)
        break
if person is None:
    person = {'name' : name, 'wins' : 0}
person['wins'] += 1
if pidx is None:
    people_list.append(person)
else
    people_list[pidx] = person

有没有更好的方法使用列表来完成这个任务?鉴于我要将它保存到MongoDB中,不能使用dict,因为它会作为对象进行保存,而我想使用本机数组函数进行排序和映射,这些函数在对象中不可用。

5个回答

14

我假设你不想使用除列表之外的任何数据结构。你的代码应该可以工作,虽然你在更新字典之后不必要地将它重新写回到列表中。字典是按引用复制的,所以一旦你更新了它,它就会在列表中保持更新状态。稍加整理后,你的代码可能如下所示:

def add_win(people_list, name):
    person = find_person(people_list, name)
    person['wins'] += 1

def find_person(people_list, name):
    for person in people_list:
        if person['name'] == name:
            return person
    person = {'name': name, 'wins': 0}
    people_list.append(person)
    return person

9

是的,使用字典。

wins = {}
for name in winners:
    wins.setdefault(name, 0)
    wins[name] += 1

编辑:

index = {}
for name in wins:
    person = index.setdefault(name, { 'name' : name, 'wins': 0 })
    if person['wins'] == 0:
        person_list.append(person)
    person['wins'] += 1

1
我不想使用dict,我想使用list。说“使用dict”是无用的,因为我希望它是一个列表。我将其保存到MongoDB中,使用dict会使其成为一个对象,而我想使用一些本地数组函数。 - Josh K
@Josh K:请看我的关于使用“列表”和“集合”的答案。 - Nick Bastin
@MAK:有什么不清楚的吗?我知道使用dict会提供更清晰的访问模式,但我不能这样做。 - Josh K
1
@Josh:答案仍然是“使用字典”。请参阅我的编辑,了解如何同时实现。 - tangentstorm

7
如果您不想永久使用一个字典,请暂时使用一个。
people = [
    {'name' : 'John', 'wins' : 10 },
    {'name' : 'Sally', 'wins' : 0 },
    {'name' : 'Fred', 'wins' : 3 },
    {'name' : 'Mary', 'wins' : 6 }
]

wins = ['Fred', 'Mary', 'Sally']

people_dict = dict((p["name"], p) for p in people)

for winner in wins:
    people_dict[winner].setdefault("wins", 0)
    people_dict[winner]["wins"] += 1

people = people_dict.values()

你能将一个 list 转换成 dict 吗? 我从MongoDB中提取了一个列表。 - Josh K
在示例代码中定义people_dict的那一行,正是发生了这种情况。键是名称,值是您拥有的字典。 - Stephen Paulger
转换为列表/字典和从列表/字典转换的处理开销是多少? - Josh K
比每次在列表中搜索要增加一个数字的处理开销要小。 - Stephen Paulger

4
您的访问模式决定了使用不同的数据结构(或至少是另一个辅助数据结构)。如果您正在使用列表,则像您所做的那样扫描列表实际上是正确的做法,但是如果您想要它效率更高,就不应该使用列表。
如果列表的顺序无关紧要,则应使用字典(Python dict)。如果有关系,则应使用来自collections模块的OrderedDict
您还可以使用两个单独的数据结构-您已经拥有的列表,以及另外一个仅包含列表中名称的set,因此您可以快速访问测试是否包含。但是,set并不能帮助您快速访问实际的名称数据(您仍然必须在列表中进行线性搜索),因此它只是一种有用的模式,如果您仅仅是测试包含性,否则总是按原样遍历列表。 编辑:似乎您实际上需要的是列表和字典,其中字典是name和列表中索引之间的映射。或者,您仍然可以使用dictOrderedDict,但是通过使用dict.iteritems()将它们插入Mongo作为数组来创建一个数组(或在Mongo中看起来像数组的东西)插入。您可以使用来自zipitertools中的各种修改器动态构建所需的对象以生成结果数组。

请注意,collections.OrderedDict 是 Python 2.7 中的新功能,但是在早期版本中有一个相当于 OrderedDict 的方案。详情请参阅文档 - eksortso

0

这个特定的案例是由collections.Counter类型实现的。除了数组生成器之外,这是一个表达式:

[{'name':name, 'wins':wins}
 for name, wins in Counter(names).items()]

如果你想要一个特定的顺序,sorted() 是最简单的方法(这也使用了普通生成器 (),而不是数组生成器 [],因为它是临时的):

sorted(({'name':name, 'wins':wins} for name, wins in Counter(names).items()),
       key=lambda item: item['name'])

item['name'] 可以是 item['wins'] 或任何其他可比较的表达式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接