Calculate difference in keys contained in two Python dictionaries

后端 未结 21 1280
眼角桃花
眼角桃花 2020-11-27 09:33

Suppose I have two Python dictionaries - dictA and dictB. I need to find out if there are any keys which are present in dictB but not

相关标签:
21条回答
  • 2020-11-27 09:58

    As Alex Martelli wrote, if you simply want to check if any key in B is not in A, any(True for k in dictB if k not in dictA) would be the way to go.

    To find the keys that are missing:

    diff = set(dictB)-set(dictA) #sets
    
    C:\Dokumente und Einstellungen\thc>python -m timeit -s "dictA =    
    dict(zip(range(1000),range
    (1000))); dictB = dict(zip(range(0,2000,2),range(1000)))" "diff=set(dictB)-set(dictA)"
    10000 loops, best of 3: 107 usec per loop
    
    diff = [ k for k in dictB if k not in dictA ] #lc
    
    C:\Dokumente und Einstellungen\thc>python -m timeit -s "dictA = 
    dict(zip(range(1000),range
    (1000))); dictB = dict(zip(range(0,2000,2),range(1000)))" "diff=[ k for k in dictB if
    k not in dictA ]"
    10000 loops, best of 3: 95.9 usec per loop
    

    So those two solutions are pretty much the same speed.

    0 讨论(0)
  • 2020-11-27 10:02

    here's a solution that can compare more than two dicts:

    def diff_dict(dicts, default=None):
        diff_dict = {}
        # add 'list()' around 'd.keys()' for python 3 compatibility
        for k in set(sum([d.keys() for d in dicts], [])):
            # we can just use "values = [d.get(k, default) ..." below if 
            # we don't care that d1[k]=default and d2[k]=missing will
            # be treated as equal
            if any(k not in d for d in dicts):
                diff_dict[k] = [d.get(k, default) for d in dicts]
            else:
                values = [d[k] for d in dicts]
                if any(v != values[0] for v in values):
                    diff_dict[k] = values
        return diff_dict
    

    usage example:

    import matplotlib.pyplot as plt
    diff_dict([plt.rcParams, plt.rcParamsDefault, plt.matplotlib.rcParamsOrig])
    
    0 讨论(0)
  • @Maxx has an excellent answer, use the unittest tools provided by Python:

    import unittest
    
    
    class Test(unittest.TestCase):
        def runTest(self):
            pass
    
        def testDict(self, d1, d2, maxDiff=None):
            self.maxDiff = maxDiff
            self.assertDictEqual(d1, d2)
    

    Then, anywhere in your code you can call:

    try:
        Test().testDict(dict1, dict2)
    except Exception, e:
        print e
    

    The resulting output looks like the output from diff, pretty-printing the dictionaries with + or - prepending each line that is different.

    0 讨论(0)
  • 2020-11-27 10:04

    In case you want the difference recursively, I have written a package for python: https://github.com/seperman/deepdiff

    Installation

    Install from PyPi:

    pip install deepdiff
    

    Example usage

    Importing

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

    Same object returns empty

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

    Type of an item has changed

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

    Value of an item has changed

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

    Item added and/or removed

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

    String difference

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

    String difference 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
    

    Type change

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

    List difference

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

    List difference 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}}}
    

    List difference ignoring order or duplicates: (with the same dictionaries as above)

    >>> 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)
    {}
    

    List that contains dictionary:

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

    Sets:

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

    Named Tuples:

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

    Custom objects:

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

    Object attribute added:

    >>> t2.c = "new attribute"
    >>> pprint(DeepDiff(t1, t2))
    {'attribute_added': ['root.c'],
     'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
    
    0 讨论(0)
  • 2020-11-27 10:04

    what about standart (compare FULL Object)

    PyDev->new PyDev Module->Module: unittest

    import unittest
    
    
    class Test(unittest.TestCase):
    
    
        def testName(self):
            obj1 = {1:1, 2:2}
            obj2 = {1:1, 2:2}
            self.maxDiff = None # sometimes is usefull
            self.assertDictEqual(d1, d2)
    
    if __name__ == "__main__":
        #import sys;sys.argv = ['', 'Test.testName']
    
        unittest.main()
    
    0 讨论(0)
  • 2020-11-27 10:04

    As mentioned in other answers, unittest produces some nice output for comparing dicts, but in this example we don't want to have to build a whole test first.

    Scraping the unittest source, it looks like you can get a fair solution with just this:

    import difflib
    import pprint
    
    def diff_dicts(a, b):
        if a == b:
            return ''
        return '\n'.join(
            difflib.ndiff(pprint.pformat(a, width=30).splitlines(),
                          pprint.pformat(b, width=30).splitlines())
        )
    

    so

    dictA = dict(zip(range(7), map(ord, 'python')))
    dictB = {0: 112, 1: 'spam', 2: [1,2,3], 3: 104, 4: 111}
    print diff_dicts(dictA, dictB)
    

    Results in:

    {0: 112,
    -  1: 121,
    -  2: 116,
    +  1: 'spam',
    +  2: [1, 2, 3],
       3: 104,
    -  4: 111,
    ?        ^
    
    +  4: 111}
    ?        ^
    
    -  5: 110}
    

    Where:

    • '-' indicates key/values in the first but not second dict
    • '+' indicates key/values in the second but not the first dict

    Like in unittest, the only caveat is that the final mapping can be thought to be a diff, due to the trailing comma/bracket.

    0 讨论(0)
提交回复
热议问题