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


71

我有两个字典。我需要找到两者之间的区别,这应该给我关键和价值。

我已经搜索并找到了一些插件/软件包,例如datadiff,dictdiff-master,但是当我在Python 2.7中尝试时,它说没有定义这样的模块。

我在这里用套装。

first_dict = {}
second_dict = {}

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

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

我只有钥匙,我什至需要获取值。


2
如果键相同但它们的值不同,您是否还需要查找差异?
蒂姆·皮茨克

Answers:


68

使用字典理解来尝试以下代码段:

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

在上面的代码中,我们找到了的差异然后重建一个dict具有相应值的键


4
由于dictset都是哈希图,因此我不知道为什么dict不能支持difference()方法set

4
这只为您提供了第二个字典中的键,而第一个字典中没有的键。那在第一而不是第二的东西呢?
henryJack

10
@henryJack您也可以执行以下操作来比较值:value = { k : second_dict[k] for k, _ in set(second_dict.items()) - set(first_dict.items()) } Doingdict.items()将元组从键值转换为值,并在设置差异中进行比较。因此,这将提供所有新键以及更改的值。
1998年

@ascrookes-是的!我将更新我的答案以反映这一点... stackoverflow.com/a/48544451/6782278
henryJack

4
我正在TypeError: unhashable type: 'dict'
wander95

98

我认为最好使用集合的对称差操作来做到一点,这是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'}

2
太好了,这几乎是Raymond Hettinger在9年前在另一个论坛上建议的相同解决方案:code.activestate.com/recipes/576644-diff-two-dictionaries/#c1
cscanlin

12
优雅的解决方案。但是它不能应用于具有不可散列值的字典。
Craynic Cai

我怀疑这个答案是OP真正需要的,应该是公认的答案。
安德烈·波特诺

6
TypeError: unhashable type: 'dict'
雷阳

5
您会收到@LeiYang,TypeError: unhashable type: 'dict'因为“顶级字典”中的值之一是另一个字典。提议的解决方案仅适用于平面词典
josebama

34

另一个解决方案是dictdifferhttps://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')])

1
最实用的调试解决方案。
ijoseph

7

您正确地看待使用集合是正确的,我们只需要更深入地研究即可使您的方法起作用。

首先,示例代码:

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'}

5

此功能仅基于字典键为您提供所有差异(以及保持不变的差异)。它还强调了一些不错的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())}

common_dict_a和common_dict_b不一样吗?A和B的共同点是一组key:value对。无需重复。
编码旋钮

2
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)])

2

如其他答案所述,使用对称差集运算符的函数可保留值的原点:

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)从这两个字典元组。

然后我们据此做出新的决定,将密钥折叠在一起并对其进行迭代。这些是从一个字典更改为下一个字典的唯一键。

然后,当这些键不存在时,我们将使用这些键以及来自每个dict的值的元组组成一个新的dict,并替换为丢失的令牌。


这很棒!但它不工作时,一个或两个类型的字典包含列表:集(a.items())^集(b.items())类型错误:unhashable类型: '名单'
Qlii256

1

那这个呢?不那么漂亮,但很明确。

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'])

-1

老问题,但以为我还是会分享我的解决方案。很简单

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对)的包装以进行处理。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.