在Python中比较多个字典

5 投票
5 回答
6744 浏览
提问于 2025-04-11 18:47

我刚开始学Python,遇到了一个问题,怎么也找不到解决办法。我用wxPython和ObjectiveListView做了一个图形界面。在这个界面的正中央,有一个列表控件,显示了用户加载的数据,分成X行和五列。

当用户在列表控件中选择多个条目时(按住CTRL或Shift再点击),ObjectiveListView模块会给我一个字典的列表,这些字典里包含了列表控件中每一行的数据。这正是我想要的,太好了!

返回的列表大概是这样的:

print MyList
[{'id':1023, 'type':'Purchase', 'date':'23.8.2008', 'sum':'-21,90', 'target':'Apple Store'}, {'id':1024, 'type':'Purchase', 'date':'24.8.2008', 'sum':'-21,90', 'target':'Apple Store'}, {'id':23, 'type':'Purchase', 'date':'2.8.2008', 'sum':'-21,90', 'target':'Apple Store'}]

所有的字典都有相同的键,但值是不同的。这里面有一个独特的'id'值。问题就从这里开始了。我想要获取用户选择的所有项目的共同值。在上面的列表中,这些共同值是'sum':'-21,90'和'target':'Apple Store'。

我不知道怎么正确地比较列表中的字典。一个大问题是,我事先不知道列表里有多少个字典,因为这是用户决定的。

我有一个模糊的想法,觉得可以用列表推导式来解决这个问题,但我只知道怎么用列表推导式比较两个列表,而不是多个列表。如果有人能帮帮我,我会非常感激。

5 个回答

2

首先,我们需要一个函数来计算两个字典的交集:

def IntersectDicts( d1, d2 ) :
    return dict(filter(lambda (k,v) : k in d2 and d2[k] == v, d1.items()))

然后我们可以用这个函数来处理任意数量的字典:

result = reduce(IntersectDicts, MyList)
8
>>> mysets = (set(x.items()) for x in MyList)
>>> reduce(lambda a,b: a.intersection(b), mysets)
set([('sum', '-21,90'), ('type', 'Purchase'), ('target', 'Apple Store')])

首先,我创建了一个生成器,它可以把字典列表转换成一个可迭代的键值对集合序列。你可以用列表推导式来做,但这样做不会把整个列表再转成一个新的列表,这在你不知道列表会有多大时特别有用。

接着,我使用了reduce函数来找出每个集合之间的共同值。它会找到集合1和集合2的交集,这个交集本身也是一个集合,然后再找这个交集和集合3的交集,依此类推。mysets生成器会根据需要把每个集合提供给reduce函数,直到完成。

我相信在Python 3.0中,reduce这个内置函数已经被弃用了,但在functools模块中仍然可以使用。

当然,你也可以把mysets替换成生成器表达式,这样可以把代码写成一行,但我觉得这样会降低可读性。实际上,我可能还会进一步把lambda函数单独放在一行:

>>> mysets = (set(x.items()) for x in MyList)
>>> find_common = lambda a,b: a.intersection(b)
>>> reduce(find_common, mysets)
set([('sum', '-21,90'), ('type', 'Purchase'), ('target', 'Apple Store')])

如果你需要最终结果是一个字典,只需像这样包装一下:

>>> dict(reduce(find_common, mysets))
{'sum': '-21,90', 'type': 'Purchase', 'target': 'Apple Store'}

dict可以接受任何键值对的迭代器,比如最后返回的元组集合。

8

我的回答和Matthew Trevor的完全一样,只有一个不同之处:

>>> mysets = (set(x.items()) for x in MyList)
>>> reduce(set.intersection, mysets)
set([('sum', '-21,90'), ('type', 'Purchase'), ('target', 'Apple Store')])

在这里,我使用了set.intersection,而不是创建一个新的lambda函数。在我看来,这样更容易理解,因为这可以直观地看作“reduce正在用集合交集操作符来处理这个列表”。这样做应该也会快很多,因为set.intersection是一个内置的C语言函数。

为了完整回答你的问题,你可以使用列表推导式来提取值:

>>> mysets = (set(x.items()) for x in MyList)
>>> result = reduce(set.intersection, mysets)
>>> values = [r[1] for r in result]
>>> values
['-21,90', 'Purchase', 'Apple Store']

对我来说,这样最终会变成一行,但这完全取决于你:

>>> [r[1] for r in reduce(set.intersection, (set(x.items()) for x in myList))]
['-21,90', 'Purchase', 'Apple Store']

撰写回答