Сравнение двух словарей в Python - PullRequest
205 голосов
/ 24 декабря 2010

У меня есть два словаря, но для упрощения я возьму эти два:

>>> 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 равны.

Ответы [ 22 ]

157 голосов
/ 24 декабря 2010

То, что вы хотите сделать, это просто 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))]

Разница только в одном элементе, но ваш алгоритм увидит, что все элементы отличаются

149 голосов
/ 24 декабря 2010

Если вы хотите узнать, сколько значений совпадают в обоих словарях, вы должны были это сказать :)

Может быть что-то вроде этого:

shared_items = {k: x[k] for k in x if k in y and x[k] == y[k]}
print len(shared_items)
137 голосов
/ 18 сентября 2013
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)
78 голосов
/ 02 декабря 2016

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

Предоставление ключевых аргументов, как в первом примере, работает только для ключи, которые являются действительными идентификаторами Python. В противном случае любые действительные ключи могут использоваться.

Действительно для py2 и py3.

53 голосов
/ 13 июня 2013

Я новичок в Python, но в итоге я сделал нечто похожее на @ mouad

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

Оператор XOR (^) должен исключить все элементы dict, если они одинаковы в обоихdicts.

44 голосов
/ 29 апреля 2015

Просто используйте:

assert cmp(dict1, dict2) == 0
26 голосов
/ 18 июня 2018

Поскольку, кажется, никто не упомянул deepdiff, я добавлю это здесь только для полноты.Я нахожу это очень удобным для получения diff (вложенных) объектов в целом.

import deepdiff
import json

dict_1 = {
    "a": 1,
    "nested": {
        "b": 1,
    }
}
dict_2 = {
    "a": 2,
    "nested": {
        "b": 2,
    }
}
diff = deepdiff.DeepDiff(dict_1, dict_2)
print(json.dumps(diff, indent=4))

Вывод:

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

ПРИМЕЧАНИЕ:

deepdiff необходимоустановлен.

    pip install deepdiff
8 голосов
/ 25 октября 2013

@ 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
6 голосов
/ 15 марта 2015

Еще одна возможность, вплоть до последней ноты OP, состоит в том, чтобы сравнивать хэши (SHA или MD) диктов, сброшенных как 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))
4 голосов
/ 04 сентября 2017

Чтобы проверить, равны ли два значения в ключах и значениях:

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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...