Сравнение двух словарей в 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 пар равны.

2385   17  

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.

просто использовать:

assert cmp(dict1, dict2) == 0

чтобы проверить, если два словаря имеют то же содержание, просто использовать:

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

вы можете пересекать, объединять, различать (показано выше), симметрично различать эти объекты представления словаря.
Лучше? Быстрее? - не уверен, но часть стандартной библиотеки, что делает ее большой плюс портативность

import json

if json.dumps(dict1) == json.dumps(dict2):
    print("Equal")

Comments

    Ничего не найдено.