【问题标题】:Getting the difference (delta) between two lists of dictionaries获取两个字典列表之间的差异(增量)
【发布时间】:2013-11-14 08:13:45
【问题描述】:

我有以下 Python 数据结构:

data1 = [{'name': u'String 1'}, {'name': u'String 2'}]
data2 = [{'name': u'String 1'}, {'name': u'String 2'}, {'name': u'String 3'}]

我正在寻找获得两个列表之间差异的最佳方法。 Python 中有没有像 JavaScript Underscore.js (_.difference) 库一样方便的东西?

【问题讨论】:

    标签: python list dictionary delta


    【解决方案1】:

    如果您想要递归地区分,我已经为 python 编写了一个包: https://github.com/seperman/deepdiff

    安装

    从 PyPi 安装:

    pip install deepdiff
    

    示例用法

    导入

    >>> from deepdiff import DeepDiff
    >>> from pprint import pprint
    >>> from __future__ import print_function # In case running on Python 2
    

    相同的对象返回空

    >>> t1 = {1:1, 2:2, 3:3}
    >>> t2 = t1
    >>> print(DeepDiff(t1, t2))
    {}
    

    项目类型已更改

    >>> t1 = {1:1, 2:2, 3:3}
    >>> t2 = {1:1, 2:"2", 3:3}
    >>> pprint(DeepDiff(t1, t2), indent=2)
    { 'type_changes': { 'root[2]': { 'newtype': <class 'str'>,
                                     'newvalue': '2',
                                     'oldtype': <class 'int'>,
                                     'oldvalue': 2}}}
    

    物品的价值发生了变化

    >>> t1 = {1:1, 2:2, 3:3}
    >>> t2 = {1:1, 2:4, 3:3}
    >>> pprint(DeepDiff(t1, t2), indent=2)
    {'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}
    

    添加和/或删除项目

    >>> t1 = {1:1, 2:2, 3:3, 4:4}
    >>> t2 = {1:1, 2:4, 3:3, 5:5, 6:6}
    >>> ddiff = DeepDiff(t1, t2)
    >>> pprint (ddiff)
    {'dic_item_added': ['root[5]', 'root[6]'],
     'dic_item_removed': ['root[4]'],
     'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}
    

    字符串区别

    >>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world"}}
    >>> t2 = {1:1, 2:4, 3:3, 4:{"a":"hello", "b":"world!"}}
    >>> ddiff = DeepDiff(t1, t2)
    >>> pprint (ddiff, indent = 2)
    { 'values_changed': { 'root[2]': {'newvalue': 4, 'oldvalue': 2},
                          "root[4]['b']": { 'newvalue': 'world!',
                                            'oldvalue': 'world'}}}
    

    字符串差异2

    >>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world!\nGoodbye!\n1\n2\nEnd"}}
    >>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n1\n2\nEnd"}}
    >>> ddiff = DeepDiff(t1, t2)
    >>> pprint (ddiff, indent = 2)
    { 'values_changed': { "root[4]['b']": { 'diff': '--- \n'
                                                    '+++ \n'
                                                    '@@ -1,5 +1,4 @@\n'
                                                    '-world!\n'
                                                    '-Goodbye!\n'
                                                    '+world\n'
                                                    ' 1\n'
                                                    ' 2\n'
                                                    ' End',
                                            'newvalue': 'world\n1\n2\nEnd',
                                            'oldvalue': 'world!\n'
                                                        'Goodbye!\n'
                                                        '1\n'
                                                        '2\n'
                                                        'End'}}}
    
    >>> 
    >>> print (ddiff['values_changed']["root[4]['b']"]["diff"])
    --- 
    +++ 
    @@ -1,5 +1,4 @@
    -world!
    -Goodbye!
    +world
     1
     2
     End
    

    类型改变

    >>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
    >>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n\n\nEnd"}}
    >>> ddiff = DeepDiff(t1, t2)
    >>> pprint (ddiff, indent = 2)
    { 'type_changes': { "root[4]['b']": { 'newtype': <class 'str'>,
                                          'newvalue': 'world\n\n\nEnd',
                                          'oldtype': <class 'list'>,
                                          'oldvalue': [1, 2, 3]}}}
    

    列表差异

    >>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3, 4]}}
    >>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
    >>> ddiff = DeepDiff(t1, t2)
    >>> pprint (ddiff, indent = 2)
    {'iterable_item_removed': {"root[4]['b'][2]": 3, "root[4]['b'][3]": 4}}
    

    列出差异2:

    >>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
    >>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
    >>> ddiff = DeepDiff(t1, t2)
    >>> pprint (ddiff, indent = 2)
    { 'iterable_item_added': {"root[4]['b'][3]": 3},
      'values_changed': { "root[4]['b'][1]": {'newvalue': 3, 'oldvalue': 2},
                          "root[4]['b'][2]": {'newvalue': 2, 'oldvalue': 3}}}
    

    忽略顺序或重复列出差异:(使用与上述相同的字典)

    >>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
    >>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
    >>> ddiff = DeepDiff(t1, t2, ignore_order=True)
    >>> print (ddiff)
    {}
    

    包含字典的列表:

    >>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:1, 2:2}]}}
    >>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:3}]}}
    >>> ddiff = DeepDiff(t1, t2)
    >>> pprint (ddiff, indent = 2)
    { 'dic_item_removed': ["root[4]['b'][2][2]"],
      'values_changed': {"root[4]['b'][2][1]": {'newvalue': 3, 'oldvalue': 1}}}
    

    套装:

    >>> t1 = {1, 2, 8}
    >>> t2 = {1, 2, 3, 5}
    >>> ddiff = DeepDiff(t1, t2)
    >>> pprint (DeepDiff(t1, t2))
    {'set_item_added': ['root[3]', 'root[5]'], 'set_item_removed': ['root[8]']}
    

    命名元组:

    >>> from collections import namedtuple
    >>> Point = namedtuple('Point', ['x', 'y'])
    >>> t1 = Point(x=11, y=22)
    >>> t2 = Point(x=11, y=23)
    >>> pprint (DeepDiff(t1, t2))
    {'values_changed': {'root.y': {'newvalue': 23, 'oldvalue': 22}}}
    

    自定义对象:

    >>> class ClassA(object):
    ...     a = 1
    ...     def __init__(self, b):
    ...         self.b = b
    ... 
    >>> t1 = ClassA(1)
    >>> t2 = ClassA(2)
    >>> 
    >>> pprint(DeepDiff(t1, t2))
    {'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
    

    添加了对象属性:

    >>> t2.c = "new attribute"
    >>> pprint(DeepDiff(t1, t2))
    {'attribute_added': ['root.c'],
     'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
    

    【讨论】:

    • 惊人的图书馆。这个特性集是如何不在 Python 标准库中的 - 我永远不会知道 ;)
    • 哈哈。谢谢@TonySepia!也许有一天它会出现在标准库中……我想我可以提交一份提案……
    【解决方案2】:
    data1 = [{'name': u'String 1'}, {'name': u'String 2'}]
    data2 = [{'name': u'String 1'}, {'name': u'String 2'}, {'name': u'String 3'}]
    
    delta = list({dict2['name'] for dict2 in data2} - 
                 {dict1['name'] for dict1 in data1})
    delta_dict = [{'name': value} for value in delta]
    print delta_dict
    

    【讨论】:

    • 你要知道data2有更多数据要设置'delta'
    【解决方案3】:

    这个怎么样:

    >>> [x for x in data2 if x not in data1]
    [{'name': u'String 3'}]
    

    编辑

    如果您需要对称差异,您可以使用:

    >>> [x for x in data1 + data2 if x not in data1 or x not in data2]
    

    >>> [x for x in data1 if x not in data2] + [y for y in data2 if y not in data1]
    

    再修改一次

    你也可以使用集合:

    >>> from functools import reduce
    >>> s1 = set(reduce(lambda x, y: x + y, [x.items() for x in data1]))
    >>> s2 = set(reduce(lambda x, y: x + y, [x.items() for x in data2]))
    
    >>> s2.difference(s1)
    >>> s2.symmetric_difference(s1)
    

    【讨论】:

    • 如果data1 包含不在data2 中的项目会怎样?
    • 正如@LutzHorn 指出的那样,这相当于set(data2) - set(data1)(除了dicts 是不可散列的)。 @user1513388 要求提供集合对称差的增量,因此正确答案是 @LutzHorn 的。
    • @LutzHorn _.difference 不是对称的,所以输出是一样的。
    • 谢谢小伙子们——以上两个建议似乎都对我有用并且产生了相同的结果。使用其中一个有什么好处吗?
    【解决方案4】:

    使用itertools.filterfalse:

    import itertools
    
    r = list(itertools.filterfalse(lambda x: x in data1, data2))
      + list(itertools.filterfalse(lambda x: x in data2, data1))
    
    assert r == [{'name': 'String 3'}]
    

    【讨论】:

    • 这是python2上的ifilterfalse。
    猜你喜欢
    • 1970-01-01
    • 2011-03-28
    • 2021-05-28
    • 2020-02-18
    • 2020-10-30
    • 2019-05-12
    • 2018-06-22
    相关资源
    最近更新 更多