簡體   English   中英

如何獲取Python中兩個字典的區別?

[英]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/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

這將返回一個新的字典(僅更改的數據)。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM