web-dev-qa-db-fra.com

Comparer deux dictionnaires en Python

J'ai deux dictionnaires, mais pour simplifier, je vais prendre ces deux:

>>> x = dict(a=1, b=2)
>>> y = dict(a=2, b=2)

Maintenant, je veux comparer si chaque paire key, value dans x a la même valeur correspondante dans y. Alors j'ai écrit ceci:

>>> for x_values, y_values in Zip(x.iteritems(), y.iteritems()):
        if x_values == y_values:
            print 'Ok', x_values, y_values
        else:
            print 'Not', x_values, y_values

Et cela fonctionne puisqu’une Tuple est renvoyée puis comparée pour égalité.

Mes questions:

Est-ce correct? Existe-t-il un meilleur moyen de le faire? Mieux vaut ne pas aller vite, je parle d'élégance du code.

UPDATE: J'ai oublié de mentionner que je dois vérifier combien de paires key, value sont égales. 

180
user225312

Si vous voulez savoir combien de valeurs correspondent dans les deux dictionnaires, vous devriez avoir dit cela :) 

Peut-être quelque chose comme ça:

shared_items = {k: x[k] for k in x if k in y and x[k] == y[k]}
print len(shared_items)
132
mouad

Ce que vous voulez faire, c'est simplement x==y 

Ce que vous faites n'est pas une bonne idée, car les éléments d'un dictionnaire ne sont censés avoir aucun ordre. Vous comparez peut-être [('a',1),('b',1)] avec [('b',1), ('a',1)] (mêmes dictionnaires, ordre différent).

Par exemple, voir ceci:

>>> x = dict(a=2, b=2,c=3, d=4)
>>> x
{'a': 2, 'c': 3, 'b': 2, 'd': 4}
>>> y = dict(b=2,c=3, d=4)
>>> y
{'c': 3, 'b': 2, 'd': 4}
>>> Zip(x.iteritems(), y.iteritems())
[(('a', 2), ('c', 3)), (('c', 3), ('b', 2)), (('b', 2), ('d', 4))]

La différence est seulement un élément, mais votre algorithme verra que tous éléments sont différents

149
Jochen Ritzel
def dict_compare(d1, d2):
    d1_keys = set(d1.keys())
    d2_keys = set(d2.keys())
    intersect_keys = d1_keys.intersection(d2_keys)
    added = d1_keys - d2_keys
    removed = d2_keys - d1_keys
    modified = {o : (d1[o], d2[o]) for o in intersect_keys if d1[o] != d2[o]}
    same = set(o for o in intersect_keys if d1[o] == d2[o])
    return added, removed, modified, same

x = dict(a=1, b=2)
y = dict(a=2, b=2)
added, removed, modified, same = dict_compare(x, y)
126
Daniel Myers

dic1 == dic2

De python docs

À titre d’illustration, les exemples suivants renvoient tous un dictionnaire equal à {"one": 1, "two": 2, "three": 3}:

>>> a = dict(one=1, two=2, three=3)
>>> b = {'one': 1, 'two': 2, 'three': 3}
>>> c = dict(Zip(['one', 'two', 'three'], [1, 2, 3]))
>>> d = dict([('two', 2), ('one', 1), ('three', 3)])
>>> e = dict({'three': 3, 'one': 1, 'two': 2})
>>> a == b == c == d == e
True

Valable pour py2 et py3.

56
Pedro Lobito

Je suis nouveau sur Python mais j'ai fini par faire quelque chose de similaire à @mouad

unmatched_item = set(dict_1.items()) ^ set(dict_2.items())
len(unmatched_item) # should be 0

L'opérateur XOR (^) devrait éliminer tous les éléments du dict lorsqu'ils sont identiques dans les deux dicts.

50
philipp

Il suffit d'utiliser:

assert cmp(dict1, dict2) == 0
42
Shiyu

Comme il semble que personne n’a mentionné deepdiff , je l’ajouterai ici uniquement pour compléter .. .. Je le trouve très pratique pour obtenir le diff des objets (imbriqués) en général.

import deepdiff
from pprint import pprint

aa = {
    "a": 1,
    "nested": {
        "b": 1,
    }
}
bb = {
    "a": 2,
    "nested": {
        "b": 2,
    }
}
pprint(deepdiff.DeepDiff(aa, bb))

Sortie:

{'values_changed': {"root['a']": {'new_value': 2, 'old_value': 1},
                "root['nested']['b']": {'new_value': 2, 'old_value': 1}}}

REMARQUE:

  • Le paquet deepdiff doit être installé car il ne s'agit pas d'un paquet standard

  • des efforts devront être consentis pour analyser le résultat


Cependant, pour prendre le dictionnaire des dictionnaires, je trouve que dictdiffer est très pratique.

14
Anubis

La réponse de @mouad est Nice si vous supposez que les deux dictionnaires ne contiennent que des valeurs simples. Toutefois, si vous avez des dictionnaires contenant des dictionnaires, vous obtenez une exception, car ils ne sont pas obligatoires.

De prime abord, quelque chose comme cela pourrait fonctionner:

def compare_dictionaries(dict1, dict2):
     if dict1 is None or dict2 is None:
        print('Nones')
        return False

     if (not isinstance(dict1, dict)) or (not isinstance(dict2, dict)):
        print('Not dict')
        return False

     shared_keys = set(dict2.keys()) & set(dict2.keys())

     if not ( len(shared_keys) == len(dict1.keys()) and len(shared_keys) == len(dict2.keys())):
        print('Not all keys are shared')
        return False


     dicts_are_equal = True
     for key in dict1.keys():
         if isinstance(dict1[key], dict) or isinstance(dict2[key], dict):
             dicts_are_equal = dicts_are_equal and compare_dictionaries(dict1[key], dict2[key])
         else:
             dicts_are_equal = dicts_are_equal and all(atleast_1d(dict1[key] == dict2[key]))

     return dicts_are_equal
7
Alexander

Une autre possibilité, jusqu’à la dernière note du PO, consiste à comparer les hachages (SHA ou MD) des dicts affichés en JSON. La façon dont les hachages sont construits garantit que, si elles sont égales, les chaînes source le sont également. C'est très rapide et mathématiquement correct. 

import json
import hashlib

def hash_dict(d):
    return hashlib.sha1(json.dumps(d, sort_keys=True)).hexdigest()

x = dict(a=1, b=2)
y = dict(a=2, b=2)
z = dict(a=1, b=2)

print(hash_dict(x) == hash_dict(y))
print(hash_dict(x) == hash_dict(z))
5
WoJ

Pour vérifier si deux clés sont égales en clés et en valeurs:

def dicts_equal(d1,d2):
    """ return True if all keys and values are the same """
    return all(k in d2 and d1[k] == d2[k]
               for k in d1) \
        and all(k in d1 and d1[k] == d2[k]
               for k in d2)

Si vous voulez renvoyer les valeurs qui diffèrent, écrivez-le différemment:

def dict1_minus_d2(d1, d2):
    """ return the subset of d1 where the keys don't exist in d2 or
        the values in d2 are different, as a dict """
    return {k,v for k,v in d1.items() if k in d2 and v == d2[k]}

Vous auriez à l'appeler deux fois i.e 

dict1_minus_d2(d1,d2).extend(dict1_minus_d2(d2,d1))
3
simonltwick

La fonction est très bien, claire et intuitive. Mais juste pour vous donner (une autre) réponse, voici mon tour:

def compare_dict(dict1, dict2):
    for x1 in dict1.keys():
        z = dict1.get(x1) == dict2.get(x1)
        if not z:
            print('key', x1)
            print('value A', dict1.get(x1), '\nvalue B', dict2.get(x1))
            print('-----\n')

Peut être utile pour vous ou pour quelqu'un d'autre ..

2
zwep

Code

def equal(a, b):
    type_a = type(a)
    type_b = type(b)

    if type_a != type_b:
        return False

    if isinstance(a, dict):
        if len(a) != len(b):
            return False
        for key in a:
            if key not in b:
                return False
            if not equal(a[key], b[key]):
                return False
        return True

    Elif isinstance(a, list):
        if len(a) != len(b):
            return False
        while len(a):
            x = a.pop()
            index = indexof(x, b)
            if index == -1:
                return False
            del b[index]
        return True

    else:
        return a == b

def indexof(x, a):
    for i in range(len(a)):
        if equal(x, a[i]):
            return i
    return -1

Tester

>>> a = {
    'number': 1,
    'list': ['one', 'two']
}
>>> b = {
    'list': ['two', 'one'],
    'number': 1
}
>>> equal(a, b)
True
2
Yas

Le code ci-dessous vous aidera à comparer la liste des dict en python 

def compate_generic_types(object1, object2):
    if isinstance(object1, str) and isinstance(object2, str):
        return object1 == object2
    Elif isinstance(object1, unicode) and isinstance(object2, unicode):
        return object1 == object2
    Elif isinstance(object1, bool) and isinstance(object2, bool):
        return object1 == object2
    Elif isinstance(object1, int) and isinstance(object2, int):
        return object1 == object2
    Elif isinstance(object1, float) and isinstance(object2, float):
        return object1 == object2
    Elif isinstance(object1, float) and isinstance(object2, int):
        return object1 == float(object2)
    Elif isinstance(object1, int) and isinstance(object2, float):
        return float(object1) == object2

    return True

def deep_list_compare(object1, object2):
    retval = True
    count = len(object1)
    object1 = sorted(object1)
    object2 = sorted(object2)
    for x in range(count):
        if isinstance(object1[x], dict) and isinstance(object2[x], dict):
            retval = deep_dict_compare(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False
        Elif isinstance(object1[x], list) and isinstance(object2[x], list):
            retval = deep_list_compare(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False
        else:
            retval = compate_generic_types(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False

    return retval

def deep_dict_compare(object1, object2):
    retval = True

    if len(object1) != len(object2):
        return False

    for k in object1.iterkeys():
        obj1 = object1[k]
        obj2 = object2[k]
        if isinstance(obj1, list) and isinstance(obj2, list):
            retval = deep_list_compare(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False

        Elif isinstance(obj1, dict) and isinstance(obj2, dict):
            retval = deep_dict_compare(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False
        else:
            retval = compate_generic_types(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False

    return retval
1
Vitthal Kadam

J'utilise cette solution qui fonctionne parfaitement pour moi dans Python 3


import logging
log = logging.getLogger(__name__)

...

    def deep_compare(self,left, right, level=0):
        if type(left) != type(right):
            log.info("Exit 1 - Different types")
            return False

        Elif type(left) is dict:
            # Dict comparison
            for key in left:
                if key not in right:
                    log.info("Exit 2 - missing {} in right".format(key))
                    return False
                else:
                    if not deep_compare(left[str(key)], right[str(key)], level +1 ):
                        log.info("Exit 3 - different children")
                        return False
            return True
        Elif type(left) is list:
            # List comparison
            for key in left:
                if key not in right:
                    log.info("Exit 4 - missing {} in right".format(key))
                    return False
                else:
                    if not deep_compare(left[left.index(key)], right[right.index(key)], level +1 ):
                        log.info("Exit 5 - different children")
                        return False
            return True
        else:
            # Other comparison
            return left == right

        return False

Il compare dict, list et tous les autres types qui implémentent l'opérateur "==" par eux-mêmes . Si vous devez comparer quelque chose de différent, vous devez ajouter une nouvelle branche dans l'arbre "if".

J'espère que cela pourra aider.

1
Giovanni Basolu

Être en retard dans ma réponse est mieux que jamais!

Comparer Not_Equal est plus efficace que de comparer Equal. En tant que tel, deux chiffres ne sont pas égaux si l'une des valeurs de clé d'un dict n'est pas trouvée dans l'autre dict. Le code ci-dessous tient compte du fait que vous comparez peut-être dict par défaut et utilise donc get au lieu de getitem []. 

Utiliser une sorte de valeur aléatoire par défaut dans l'appel get égal à la clé en cours de récupération - juste au cas où les dicts ont une valeur None comme valeur dans un dict et que cette clé n'existe pas dans l'autre. De plus, la condition get! = Est vérifiée avant la non-condition, car vous effectuez la vérification des clés et des valeurs des deux côtés en même temps.

def Dicts_Not_Equal(first,second):
    """ return True if both do not have same length or if any keys and values are not the same """
    if len(first) == len(second): 
        for k in first:
            if first.get(k) != second.get(k,k) or k not in second: return (True)
        for k in second:         
            if first.get(k,k) != second.get(k) or k not in first: return (True)
        return (False)   
    return (True)
0
user-asterix

voir les objets de la vue dictionnaire: https://docs.python.org/2/library/stdtypes.html#dict

De cette façon, vous pouvez soustraire dictView2 à dictView1 et il retournera un ensemble de paires clé/valeur différentes dans dictView2:

original = {'one':1,'two':2,'ACTION':'ADD'}
originalView=original.viewitems()
updatedDict = {'one':1,'two':2,'ACTION':'REPLACE'}
updatedDictView=updatedDict.viewitems()
delta=original | updatedDict
print delta
>>set([('ACTION', 'REPLACE')])

Vous pouvez intersecter, union, différence (voir ci-dessus), différence symétrique de ces objets en vue dictionnaire.
Meilleur? Plus rapide? - pas sûr, mais une partie de la bibliothèque standard - ce qui en fait un atout majeur pour la portabilité

0
tranimatronic

En Python 3.6, cela peut être fait comme: -

if (len(dict_1)==len(dict_2): 
  for i in dict_1.items():
        ret=bool(i in dict_2.items())

ret variable sera vraie si tous les éléments de dict_1 sont présents dans dict_2

0
Souravi Sinha
>>> hash_1
{'a': 'foo', 'b': 'bar'}
>>> hash_2
{'a': 'foo', 'b': 'bar'}
>>> set_1 = set (hash_1.iteritems())
>>> set_1
set([('a', 'foo'), ('b', 'bar')])
>>> set_2 = set (hash_2.iteritems())
>>> set_2
set([('a', 'foo'), ('b', 'bar')])
>>> len (set_1.difference(set_2))
0
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...    print "The two hashes match."
...
The two hashes match.
>>> hash_2['c'] = 'baz'
>>> hash_2
{'a': 'foo', 'c': 'baz', 'b': 'bar'}
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...     print "The two hashes match."
...
>>>
>>> hash_2.pop('c')
'baz'

Voici une autre option:

>>> id(hash_1)
140640738806240
>>> id(hash_2)
140640738994848

Donc, comme vous le voyez, les deux identifiants sont différents. Mais les riches opérateurs de comparaison semblent faire l'affaire:

>>> hash_1 == hash_2
True
>>>
>>> hash_2
{'a': 'foo', 'b': 'bar'}
>>> set_2 = set (hash_2.iteritems())
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...     print "The two hashes match."
...
The two hashes match.
>>>
0
Led Zeppelin

Dans PyUnit, il existe une méthode permettant de comparer les dictionnaires de manière élégante. Je l'ai testé à l'aide des deux dictionnaires suivants et il fait exactement ce que vous recherchez.

d1 = {1: "value1",
      2: [{"subKey1":"subValue1",
           "subKey2":"subValue2"}]}
d2 = {1: "value1",
      2: [{"subKey2":"subValue2",
           "subKey1": "subValue1"}]
      }


def assertDictEqual(self, d1, d2, msg=None):
        self.assertIsInstance(d1, dict, 'First argument is not a dictionary')
        self.assertIsInstance(d2, dict, 'Second argument is not a dictionary')

        if d1 != d2:
            standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
            diff = ('\n' + '\n'.join(difflib.ndiff(
                           pprint.pformat(d1).splitlines(),
                           pprint.pformat(d2).splitlines())))
            standardMsg = self._truncateMessage(standardMsg, diff)
            self.fail(self._formatMessage(msg, standardMsg))

Je ne recommande pas d'importer unittest dans votre code de production. Ma pensée est que la source dans PyUnit pourrait être modifiée pour fonctionner en production. Il utilise pprint qui "joliment imprimé" les dictionnaires. Il semble assez facile d'adapter ce code pour qu'il soit "prêt pour la production".

0
MikeyE

Voici ma réponse, utilisez une méthode récursive:

def dict_equals(da, db):
    if not isinstance(da, dict) or not isinstance(db, dict):
        return False
    if len(da) != len(db):
        return False
    for da_key in da:
        if da_key not in db:
            return False
        if not isinstance(db[da_key], type(da[da_key])):
            return False
        if isinstance(da[da_key], dict):
            res = dict_equals(da[da_key], db[da_key])
            if res is False:
                return False
        Elif da[da_key] != db[da_key]:
            return False
    return True

a = {1:{2:3, 'name': 'cc', "dd": {3:4, 21:"nm"}}}
b = {1:{2:3, 'name': 'cc', "dd": {3:4, 21:"nm"}}}
print dict_equals(a, b)

J'espère que cela pourra aider!

0
William Xu