如何得到 Python 中两个字典之间的区别?

我有两个字典,我需要找出它们之间的区别,这应该会给我一个键和一个值。

我已经搜索并找到了一些插件/包,比如 datadiff 和 dicdiff-master,但是当我试图在 Python 2.7中导入它们时,它说没有定义这样的模块。

我在这里用了一套:

first_dict = {}
second_dict = {}
 

value = set(second_dict) - set(first_dict)
print value

我的输出是:

>>> set(['SCD-3547', 'SCD-3456'])

我只得到键,我还需要得到值。

232107 次浏览

使用字典理解,尝试下面的代码片段:

value = { k : second_dict[k] for k in set(second_dict) - set(first_dict) }

在上面的代码中,我们找到差值 钥匙,然后使用相应的值重新构建 dict

你看到使用一个集合是正确的,我们只是需要深入一点,让你的方法工作。

首先,示例代码:

test_1 = {"foo": "bar", "FOO": "BAR"}
test_2 = {"foo": "bar", "f00": "b@r"}

我们现在可以看到,两个字典都包含一个类似的键/值对:

{"foo": "bar", ...}

每个字典还包含一个完全不同的键值对。但我们如何发现其中的差别呢?字典可不支持这种说法。相反,您将希望使用一组。

下面是如何将每本字典变成一套我们可以使用的字典:

set_1 = set(test_1.items())
set_2 = set(test_2.items())

这将返回一个包含一系列元组的集合,每个元组代表字典中的一个键/值对。

现在,要找出 set _ 1和 set _ 2之间的区别:

print set_1 - set_2
>>> {('FOO', 'BAR')}

想要回字典吗? 简单,只要:

dict(set_1 - set_2)
>>> {'FOO': 'BAR'}

我认为最好使用集合的对称差运算来做这个 abc 0。

>>> dict1 = {1:'donkey', 2:'chicken', 3:'dog'}
>>> dict2 = {1:'donkey', 2:'chimpansee', 4:'chicken'}
>>> set1 = set(dict1.items())
>>> set2 = set(dict2.items())
>>> set1 ^ set2
{(2, 'chimpansee'), (4, 'chicken'), (2, 'chicken'), (3, 'dog')}

它之所以是对称的,是因为:

>>> set2 ^ set1
{(2, 'chimpansee'), (4, 'chicken'), (2, 'chicken'), (3, 'dog')}

在使用 差分算符时,情况并非如此。

>>> set1 - set2
{(2, 'chicken'), (3, 'dog')}
>>> set2 - set1
{(2, 'chimpansee'), (4, 'chicken')}

然而,将结果集转换为字典可能不是一个好主意,因为您可能会丢失信息:

>>> dict(set1 ^ set2)
{2: 'chicken', 3: 'dog', 4: 'chicken'}

另一种解决方案是 dictdiffer(https://github.com/inveniosoftware/dictdiffer)。

import dictdiffer


a_dict = {
'a': 'foo',
'b': 'bar',
'd': 'barfoo'
}


b_dict = {
'a': 'foo',
'b': 'BAR',
'c': 'foobar'
}


for diff in list(dictdiffer.diff(a_dict, b_dict)):
print diff

Diff 是一个元组,包含更改的类型、更改的值和条目的路径。

('change', 'b', ('bar', 'BAR'))
('add', '', [('c', 'foobar')])
('remove', '', [('d', 'barfoo')])

此函数仅基于字典键给出所有差异(以及保持不变的差异)。它还突出显示了一些不错的 Dect 理解、 Set 操作和 python 3.6类型注释:)

from typing import Dict, Any, Tuple
def get_dict_diffs(a: Dict[str, Any], b: Dict[str, Any]) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any], Dict[str, Any]]:


added_to_b_dict: Dict[str, Any] = {k: b[k] for k in set(b) - set(a)}
removed_from_a_dict: Dict[str, Any] = {k: a[k] for k in set(a) - set(b)}
common_dict_a: Dict[str, Any] = {k: a[k] for k in set(a) & set(b)}
common_dict_b: Dict[str, Any] = {k: b[k] for k in set(a) & set(b)}
return added_to_b_dict, removed_from_a_dict, common_dict_a, common_dict_b

如果你想比较字典 价值观:

values_in_b_not_a_dict = {k : b[k] for k, _ in set(b.items()) - set(a.items())}

这个怎么样? 不是很漂亮,但是很露骨。

orig_dict = {'a' : 1, 'b' : 2}
new_dict = {'a' : 2, 'v' : 'hello', 'b' : 2}


updates = {}
for k2, v2 in new_dict.items():
if k2 in orig_dict:
if v2 != orig_dict[k2]:
updates.update({k2 : v2})
else:
updates.update({k2 : v2})


#test it
#value of 'a' was changed
#'v' is a completely new entry
assert all(k in updates for k in ['a', 'v'])
def flatten_it(d):
if isinstance(d, list) or isinstance(d, tuple):
return tuple([flatten_it(item) for item in d])
elif isinstance(d, dict):
return tuple([(flatten_it(k), flatten_it(v)) for k, v in sorted(d.items())])
else:
return d


dict1 = {'a': 1, 'b': 2, 'c': 3}
dict2 = {'a': 1, 'b': 1}


print set(flatten_it(dict1)) - set(flatten_it(dict2)) # set([('b', 2), ('c', 3)])
# or
print set(flatten_it(dict2)) - set(flatten_it(dict1)) # set([('b', 1)])

一个使用对称差运算符的函数,正如其他答案中提到的,它保留了值的起源:

def diff_dicts(a, b, missing=KeyError):
"""
Find keys and values which differ from `a` to `b` as a dict.


If a value differs from `a` to `b` then the value in the returned dict will
be: `(a_value, b_value)`. If either is missing then the token from
`missing` will be used instead.


:param a: The from dict
:param b: The to dict
:param missing: A token used to indicate the dict did not include this key
:return: A dict of keys to tuples with the matching value from a and b
"""
return {
key: (a.get(key, missing), b.get(key, missing))
for key in dict(
set(a.items()) ^ set(b.items())
).keys()
}

例子

print(diff_dicts({'a': 1, 'b': 1}, {'b': 2, 'c': 2}))


# {'c': (<class 'KeyError'>, 2), 'a': (1, <class 'KeyError'>), 'b': (1, 2)}

这是怎么回事

我们使用对称差集运算符来处理从项目中生成的元组。这将从两个字母表生成一组不同的 (key, value)元组。

然后,我们根据它制作一个新的命令,将这些键折叠在一起并迭代它们。这些是唯一的钥匙,已经改变了从一个字典到下一个。

然后,当密钥不存在时,我们使用这些密钥和来自每个密钥的值的元组替换我们缺少的标记来组成一个新的密钥。

老问题了,但我还是想分享一下我的解决方案,很简单。

dicta_set = set(dicta.items()) # creates a set of tuples (k/v pairs)
dictb_set = set(dictb.items())
setdiff = dictb_set.difference(dicta_set) # any set method you want for comparisons
for k, v in setdiff: # unpack the tuples for processing
print(f"k/v differences = {k}: {v}")

这段代码创建了两组表示 k/v 对的元组。然后,它使用您选择的 set 方法来比较元组。最后,它为处理解包元组(k/v 对)。

这将返回一个新的 dict (仅更改数据)。

def get_difference(obj_1: dict, obj_2: dict) -> dict:
result = {}


for key in obj_1.keys():
value = obj_1[key]


if isinstance(value, dict):
difference = get_difference(value, obj_2.get(key, {}))


if difference:
result[key] = difference


elif value != obj_2.get(key):
result[key] = obj_2.get(key, None)


return result

不确定这是不是 OP 要求的,但是当我遇到这个问题的时候,我正在寻找这个问题——特别是,如何按键显示两个字母之间的区别:

陷阱: 当一个 dict 有一个丢失的键,而另一个的值为 Nothing 时,函数会假定它们是相似的

这根本没有优化-适合于小字母

def diff_dicts(a, b, drop_similar=True):
res = a.copy()


for k in res:
if k not in b:
res[k] = (res[k], None)


for k in b:
if k in res:
res[k] = (res[k], b[k])
else:
res[k] = (None, b[k])


if drop_similar:
res = {k:v for k,v in res.items() if v[0] != v[1]}


return res




print(diff_dicts({'a': 1}, {}))
print(diff_dicts({'a': 1}, {'a': 2}))
print(diff_dicts({'a': 2}, {'a': 2}))
print(diff_dicts({'a': 2}, {'b': 2}))
print(diff_dicts({'a': 2}, {'a': 2, 'b': 1}))

产出:

{'a': (1, None)}
{'a': (1, 2)}
{}
{'a': (2, None), 'b': (None, 2)}
{'b': (None, 1)}

解决方案是使用 unittest模块:

from unittest import TestCase
TestCase().assertDictEqual(expected_dict, actual_dict)

如何在 python 中测试两个字典是否等同于 pytest获得

这是我自己的版本,从结合 https://stackoverflow.com/a/67263119/919692https://stackoverflow.com/a/48544451/919692,现在我看到它非常类似于 https://stackoverflow.com/a/47433207/919692:

def dict_diff(dict_a, dict_b, show_value_diff=True):
result = {}
result['added']   = {k: dict_b[k] for k in set(dict_b) - set(dict_a)}
result['removed'] = {k: dict_a[k] for k in set(dict_a) - set(dict_b)}
if show_value_diff:
common_keys =  set(dict_a) & set(dict_b)
result['value_diffs'] = {
k:(dict_a[k], dict_b[k])
for k in common_keys
if dict_a[k] != dict_b[k]
}
return result

我建议使用优秀开发人员已经编写的内容。就像 pytest。它可以处理任何数据类型,而不仅仅是字母。顺便说一句,pytest非常擅长测试。

from _pytest.assertion.util import _compare_eq_any


print('\n'.join(_compare_eq_any({'a': 'b'}, {'aa': 'vv'}, verbose=3)))

产出为:

Left contains 1 more item:
{'a': 'b'}
Right contains 1 more item:
{'aa': 'vv'}
Full diff:
- {'aa': 'vv'}
?    -    ^^
+ {'a': 'b'}
?        ^

如果您不喜欢使用私有函数(从 _开始) ,只需查看源代码并将该函数复制/粘贴到代码中。

附注: 用 pytest==6.2.4测试

你可以使用 DeepDiff:

pip install deepdiff

在其他方面,它允许您递归计算字典、可迭代文件、字符串和其他对象之间的差异:

>>> from deepdiff import DeepDiff


>>> d1 = {1:1, 2:2, 3:3, "foo":4}
>>> d2 = {1:1, 2:4, 3:3, "bar":5, 6:6}
>>> DeepDiff(d1, d2)
{'dictionary_item_added': [root['bar'], root[6]],
'dictionary_item_removed': [root['foo']],
'values_changed': {'root[2]': {'new_value': 4, 'old_value': 2}}}

它可以让您看到哪些改变了(甚至类型) ,哪些添加了,哪些删除了。它还允许您做许多其他事情,比如忽略重复项和忽略路径(由 regex 定义)。

下面是一个变体,如果您知道 dict2中的值是正确的,它可以让您更新 dict1值。

考虑一下:

dict1.update((k, dict2.get(k)) for k, v in dict1.items())

一方面,你可以使用字典理解:

dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
dict2 = {'a': OMG, 'b': 2, 'c': 3, 'd': 4}


data = {a:dict1[a] for a in dict1 if dict1[a] != dict2[a]}

输出: {‘ a’: 1}

sharedmLst = set(a_dic.items()).intersection(b_dic.items())
diff_from_b = set(a_dic.items()) - sharedmLst
diff_from_a = set(b_dic.items()) - sharedmLst


print("Among the items in a_dic, the item different from b_dic",diff_from_b)
print("Among the items in b_dic, the item different from a_dic",diff_from_a)

Result :
Among the items in a_dic, the item different from b_dic {('b', 2)}
Among the items in b_dic, the item different from a_dic {('b', 20)}