Сравнение двух словарей в Python
у меня есть два словаря, но для упрощения я возьму эти два:
>>> x = dict(a=1, b=2)
>>> y = dict(a=2, b=2)
теперь я хочу сравнить, является ли каждый key, value в паре x имеет такое же соответствующее значение в y. Поэтому я написал следующее:
>>> 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
и он работает с tuple возвращается и затем сравнены для равенства.
мои вопросы:
это правильно? Есть ли лучше как это сделать? Лучше не в скорости, я говорю о коде изящество.
обновление: я забыл упомянуть, что я должен проверить, сколько key, value пар равны.
17 ответов:
Если вы хотите знать, сколько значений совпадают в обоих словарях, вы должны были сказать, что:)
может, что-то вроде этого:
shared_items = {k: x[k] for k in x if k in y and x[k] == y[k]} print len(shared_items)
что вы хотите сделать, это просто
x==yто, что вы делаете, не является хорошей идеей, потому что элементы в словаре не должны иметь никакого порядка. Вы можете сравнивать
[('a',1),('b',1)]С[('b',1), ('a',1)](те же словари, разного порядка).например, вижу так:
>>> 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))]разница только один пункт, но ваш алгоритм будет видеть, что все предметы разные
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)
Я новичок в python, но в итоге я сделал что-то похожее на @mouad
unmatched_item = set(dict_1.items()) ^ set(dict_2.items()) len(unmatched_item) # should be 0оператор XOR (
^) следует исключить все элементы dict, когда они одинаковы в обоих dicts.
чтобы проверить, если два словаря имеют то же содержание, просто использовать:
dic1 == dic2С python docs:
чтобы проиллюстрировать, в следующих примерах все возвращают словарь, равный
{"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
ответ@mouad хорош, если вы предполагаете, что оба словаря просто содержат простые значения. Однако если у вас есть словари, которые содержат словари, вы получите исключение, поскольку словари не хэшируются.
С моей головы, что-то вроде этого может работать:
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
еще одна возможность, вплоть до последней ноты OP, заключается в сравнении хэшей (
SHAилиMD) из dicts сбрасывается как JSON. Способ построения хэшей гарантирует, что если они равны, исходные строки также равны. Это очень быстро и математически обоснованно.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))
так как кажется, никто не упомянул
deepdiff, Я добавлю его здесь только для полноты. Я считаю, что это очень удобно для получения diff (вложенных) объектов в целом.import deepdiff from pprint import pprint aa = { "a": 1, "nested": { "b": 1, } } bb = { "a": 2, "nested": { "b": 2, } } pprint(deepdiff.DeepDiff(aa, bb))выход:
{'values_changed': {"root['a']": {'new_value': 2, 'old_value': 1}, "root['nested']['b']": {'new_value': 2, 'old_value': 1}}}Примечание:
deepdiffпакет должен быть установлен, как это не стандартный пакетнекоторые усилия должны быть приложены для разбора результат
однако, для принятия различия словарей, я нахожу
dictdifferочень удобно.
код
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
чтобы проверить, равны ли два Дикта в ключах и значениях:
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)Если вы хотите вернуть значения, которые отличаются, пишут его по-разному:
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]}вы должны были бы назвать его дважды т. е.
dict1_minus_d2(d1,d2).extend(dict1_minus_d2(d2,d1))
функция отлично ИМО, ясно и интуитивно. Но просто чтобы дать вам (еще один) ответ, вот мой:
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')может быть полезно для вас или для кого-либо еще..
>>> 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'вот еще вариант:
>>> id(hash_1) 140640738806240 >>> id(hash_2) 140640738994848Так как вы видите, что два идентификатора отличаются. Но это богатые операторы сравнения кажется, сделать трюк:
>>> 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. >>>
В PyUnit есть метод, который сравнивает словари красиво. Я протестировал его с помощью следующих двух словарей, и он делает именно то, что вы ищете.
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))Я не рекомендую импорта
unittestна ваш рабочий код. Моя мысль-источник в PyUnit может быть повторно использован для запуска в производство. Он используетpprintкоторый "довольно печатает" словари. Кажется, довольно легко адаптировать этот код, чтобы быть "готов".
в Python 3.6, это можно сделать так:-
if (len(dict_1)==len(dict_2): for i in dict_1.items(): ret=bool(i in dict_2.items())переменная ret будет истинной, если все элементы dict_1 присутствуют в dict_2
вид объектов словарь : https://docs.python.org/2/library/stdtypes.html#dict
таким образом, вы можете вычесть dictView2 из dictView1, и он вернет набор пар ключ / значение, которые отличаются в 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')])вы можете пересекать, объединять, различать (показано выше), симметрично различать эти объекты представления словаря.
Лучше? Быстрее? - не уверен, но часть стандартной библиотеки, что делает ее большой плюс портативность
Comments