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.
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)
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
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)
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
.
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.
Il suffit d'utiliser:
assert cmp(dict1, dict2) == 0
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.
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
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))
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))
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 ..
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
>>> a = {
'number': 1,
'list': ['one', 'two']
}
>>> b = {
'list': ['two', 'one'],
'number': 1
}
>>> equal(a, b)
True
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
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.
Ê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)
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é
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
>>> 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.
>>>
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".
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!