[英]How to get the difference between two dictionaries in Python?
我有兩本字典,我需要找到兩者之間的區別,這應該給我一個鍵和一個值。
我已經搜索並找到了一些插件/包,如 datadiff 和 dictdiff-master,但是當我嘗試在 Python 2.7 中導入它們時,它說沒有定義這樣的模塊。
我在這里使用了一套:
first_dict = {}
second_dict = {}
value = set(second_dict) - set(first_dict)
print value
我的 output 是:
>>> set(['SCD-3547', 'SCD-3456'])
我只得到鍵,我還需要得到值。
我認為最好使用集合的對稱差分操作來做到這一點這里是 doc 的鏈接。
>>> 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'}
使用字典理解嘗試以下代碼段:
value = { k : second_dict[k] for k in set(second_dict) - set(first_dict) }
在上面的代碼中,我們找到了鍵的差異,然后使用相應的值重建一個dict
。
另一種解決方案是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')])
您認為使用集合是正確的,我們只需要更深入地挖掘您的方法即可。
首先,示例代碼:
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'}
此功能僅基於字典鍵為您提供所有差異(以及保持不變的差異)。 它還突出了一些不錯的 Dict 理解、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())}
如其他答案中所述,使用對稱差分集運算符的函數,它保留了值的來源:
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)}
我們對取項目生成的元組使用對稱差分集算子。 這會從兩個 dicts 生成一組不同的(key, value)
元組。
然后我們從中創建一個新的 dict 以將鍵折疊在一起並對其進行迭代。 這些是唯一從一個字典更改為下一個字典的鍵。
然后,我們使用這些鍵組成一個新的字典,其中每個字典的值的元組在鍵不存在時替換我們丟失的令牌。
不確定這是 OP 所要求的,但這是我遇到這個問題時一直在尋找的 - 具體來說,如何按鍵顯示兩個字典之間的區別:
陷阱:當一個 dict 缺少鍵,而第二個具有 None 值時,該函數將假定它們是相似的
這根本沒有優化 - 適用於小字典
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)
獲得自How can you test that two dictionary are equal with pytest in python
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)])
您可以使用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}}}
它可以讓您查看更改的內容(甚至類型)、添加的內容和刪除的內容。 它還允許您執行許多其他操作,例如忽略重復項和路徑(由正則表達式定義)。
那這個呢? 不那么漂亮但明確。
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'])
我建議使用已經由優秀開發人員編寫的東西。 像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'}
? ^
如果您不喜歡使用私有函數(以_
開頭),只需查看源代碼並將該函數復制/粘貼到您的代碼中即可。
PS:用pytest==6.2.4
測試
老問題,但無論如何我想我會分享我的解決方案。 很簡單。
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 對的元組。 然后它使用您選擇的一組方法來比較元組。 最后,它解包元組(k/v 對)進行處理。
這是我自己的版本,結合https://stackoverflow.com/a/67263119/919692和https://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
這將返回一個新的字典(僅更改的數據)。
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
這是一個變體,如果您知道 dict2 中的值是正確的,則可以更新 dict1 值。
考慮:
dict1.update((k, dict2.get(k)) for k, v in dict1.items())
對於一側比較,您可以使用 dict 理解:
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]}
output:{'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)}
此解決方案與不可散列的字典完美配合,修復了此錯誤:
TypeError: Unhashable type 'dict'.
從@Roedy 的頂級解決方案開始。 我們創建了一個列表字典,這是不可散列的一個很好的例子:
>>> dict1 = {1:['donkey'], 2:['chicken'], 3:['dog']}
>>> dict2 = {1:['donkey'], 2:['chimpansee'], 4:['chicken']}
然后我們使用str(value)
預處理使每個值可散列:
>>> set1 = set([(key, str(value)) for key, value in dict1.items()])
>>> set2 = set([(key, str(value)) for key, value in dict2.items()])
然后我們按照@Reody 的回答繼續:
>>> set1 ^ set2
{(3, "['dog']"), (4, "['chicken']"), (2, "['chimpansee']"), (2,"['chicken']")}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.