在Python中高效地查找两个字典之间的所有差异该怎么做?

3

我有两个字典,需要检查缺失的键和匹配的键,并检查它们是否具有相同或不同的值。

dict1 = {..}
dict2 = {..}
#key values in a list that are missing in each
missing_in_dict1_but_in_dict2 = []
missing_in_dict2_but_in_dict1 = []
#key values in a list that are mismatched between the 2 dictionaries
mismatch = []

什么是最有效的方法来做这件事?

这个问题在这里得到了更好的回答:https://dev59.com/CVwY5IYBdhLWcg3wRF9S#32815681 - PeJota
2个回答

9
您可以使用 字典视图对象,它们的作用类似于集合。通过对集合进行差运算来获得差异:
missing_in_dict1_but_in_dict2 = dict2.keys() - dict1
missing_in_dict2_but_in_dict1 = dict1.keys() - dict2

对于相同的键,使用交集运算符&进行处理:
mismatch = {key for key in dict1.keys() & dict2 if dict1[key] != dict2[key]}

如果您仍在使用Python 2,请使用dict.viewkeys()

使用字典视图生成交集和差异非常高效,视图对象本身非常轻量级,从集合操作创建新集合的算法可以直接利用底层字典的O(1)查找行为。

演示:

>>> dict1 = {'foo': 42, 'bar': 81}
>>> dict2 = {'bar': 117, 'spam': 'ham'}
>>> dict2.keys() - dict1
{'spam'}
>>> dict1.keys() - dict2
{'foo'}
>>> [key for key in dict1.keys() & dict2 if dict1[key] != dict2[key]]
{'bar'}

并且与创建单独的set()对象进行性能比较:

>>> import timeit
>>> import random
>>> def difference_views(d1, d2):
...     missing1 = d2.keys() - d1
...     missing2 = d1.keys() - d2
...     mismatch = {k for k in d1.keys() & d2 if d1[k] != d2[k]}
...     return missing1, missing2, mismatch
...
>>> def difference_sets(d1, d2):
...     missing1 = set(d2) - set(d1)
...     missing2 = set(d1) - set(d2)
...     mismatch = {k for k in set(d1) & set(d2) if d1[k] != d2[k]}
...     return missing1, missing2, mismatch
...
>>> testd1 = {random.randrange(1000000): random.randrange(1000000) for _ in range(10000)}
>>> testd2 = {random.randrange(1000000): random.randrange(1000000) for _ in range(10000)}
>>> timeit.timeit('d(d1, d2)', 'from __main__ import testd1 as d1, testd2 as d2, difference_views as d', number=1000)
1.8643521590274759
>>> timeit.timeit('d(d1, d2)', 'from __main__ import testd1 as d1, testd2 as d2, difference_sets as d', number=1000)
2.811345119960606

使用 set() 对象会更慢,特别是当你的输入字典变得更大时。


仅适用于Python 3.x吗? - Duncan
@Duncan:不,Python 2 中也有,作为 dict.viewkeys() - Martijn Pieters
[key for key in dict1.keys() & dict2 if dict1[key] != dict2[key]] 这段代码的作用是返回两个字典中键相同但对应值不同的键列表。而 & dict2 的作用是将 dict1 中与 dict2 中共有的键提取出来,以便进行比较。 - jo2083248
它创建了两个键集的交集,即两个字典都拥有的键。 - Martijn Pieters

2

一种简单的方法是从 dict 的键中创建集合并进行差集操作:

>>> dict1 = { 'a': 1, 'b': 1 }
>>> dict2 = { 'b': 1, 'c': 1 }
>>> missing_in_dict1_but_in_dict2 = set(dict2) - set(dict1)
>>> missing_in_dict1_but_in_dict2
set(['c'])
>>> missing_in_dict2_but_in_dict1 = set(dict1) - set(dict2)
>>> missing_in_dict2_but_in_dict1
set(['a'])

你也可以使用.difference()来避免将第二个dict转换为set

>>> set(dict1).difference(dict2)
set(['a'])
>>> set(dict2).difference(dict1)
set(['c'])

我认为,set().difference()更有效率,因为不需要进行类型转换。 - jo2083248
我不知道这是否有任何影响。此外,Martijn的回答更好。 - Duncan
1
set().difference()set().intersection()稍微快一些,因为你可以避免创建第二个set()对象。不过,字典视图再次以相同的因素更快。 - Martijn Pieters

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