Рассчитать разницу в ключах, содержащихся в двух словарях Python

171

Предположим, у меня есть два словаря Python - dictAи dictB. Мне нужно выяснить, есть ли какие-либо ключи, которые присутствуют в, dictBно не в dictA. Какой самый быстрый способ сделать это?

Должен ли я преобразовать ключи словаря в набор, а затем пойти дальше?

Интересно узнать ваши мысли ...


Спасибо за ваши ответы.

Извиняюсь за неправильное изложение моего вопроса. Мой сценарий такой: у меня есть то, dictAчто может быть таким же, dictBили может не хватать некоторых ключей по сравнению с dictBдругими, иначе значение некоторых ключей может отличаться, которое должно быть установлено на dictAзначение ключа.

Проблема в том, что словарь не имеет стандарта и может иметь значения, которые могут быть диктованными.

Сказать

dictA={'key1':a, 'key2':b, 'key3':{'key11':cc, 'key12':dd}, 'key4':{'key111':{....}}}
dictB={'key1':a, 'key2:':newb, 'key3':{'key11':cc, 'key12':newdd, 'key13':ee}.......

Таким образом, значение 'key2' должно быть сброшено на новое значение, а значение 'key13' должно быть добавлено в dict. Значение ключа не имеет фиксированного формата. Это может быть простая ценность или диктат или диктат диктата.

Натан Дэвис
источник

Ответы:

234

Вы можете использовать операции над клавишами:

diff = set(dictb.keys()) - set(dicta.keys())

Вот класс, чтобы найти все возможности: что было добавлено, что было удалено, какие пары ключ-значение одинаковы и какие пары ключ-значение изменены.

class DictDiffer(object):
    """
    Calculate the difference between two dictionaries as:
    (1) items added
    (2) items removed
    (3) keys same in both but changed values
    (4) keys same in both and unchanged values
    """
    def __init__(self, current_dict, past_dict):
        self.current_dict, self.past_dict = current_dict, past_dict
        self.set_current, self.set_past = set(current_dict.keys()), set(past_dict.keys())
        self.intersect = self.set_current.intersection(self.set_past)
    def added(self):
        return self.set_current - self.intersect 
    def removed(self):
        return self.set_past - self.intersect 
    def changed(self):
        return set(o for o in self.intersect if self.past_dict[o] != self.current_dict[o])
    def unchanged(self):
        return set(o for o in self.intersect if self.past_dict[o] == self.current_dict[o])

Вот пример вывода:

>>> a = {'a': 1, 'b': 1, 'c': 0}
>>> b = {'a': 1, 'b': 2, 'd': 0}
>>> d = DictDiffer(b, a)
>>> print "Added:", d.added()
Added: set(['d'])
>>> print "Removed:", d.removed()
Removed: set(['c'])
>>> print "Changed:", d.changed()
Changed: set(['b'])
>>> print "Unchanged:", d.unchanged()
Unchanged: set(['a'])

Доступен как репозиторий github: https://github.com/hughdbrown/dictdiffer

hughdbrown
источник
3
Умное решение, спасибо! Я заставил его работать с вложенными диктовками, проверив, являются ли измененные или неизмененные значения экземплярами dict, и вызвав рекурсивную функцию, чтобы проверить их снова, используя ваш класс.
AJJ
1
@AJJ Я бы хотел увидеть эту реализацию.
Уршрей
1
Как насчет того def update(self, new_dict): self.__init__(new_dict, self.current_dict)или иного, чтобы вы могли сделать скользящее сравнение
Ник Т
Несколько замечаний: DictDifferкласс является классом без состояний и может быть функцией. Значения changedи unchangedмогут быть вычислены в одном цикле. Эти две функции могут возвращать listвместо a, setчто, безусловно, дешевле. Для глубокого сравнения вы можете взглянуть на среду модульного тестирования: docs.python.org/2/library/unittest.html , просто следуйте assertDictEqualметоду в исходном коде.
Лоран Лапорт
1
FWIW, set(dictb)вероятно, лучше, чем set(dictb.keys()).
Мгилсон
60

Если вы хотите рекурсивное различие, я написал пакет для Python: https://github.com/seperman/deepdiff

Монтаж

Установить из PyPi:

pip install deepdiff

Пример использования

Импорт

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function # In case running on Python 2

Тот же объект возвращает пустой

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = t1
>>> print(DeepDiff(t1, t2))
{}

Тип предмета изменился

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{ 'type_changes': { 'root[2]': { 'newtype': <class 'str'>,
                                 'newvalue': '2',
                                 'oldtype': <class 'int'>,
                                 'oldvalue': 2}}}

Стоимость предмета изменилась

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

Товар добавлен и / или удален

>>> t1 = {1:1, 2:2, 3:3, 4:4}
>>> t2 = {1:1, 2:4, 3:3, 5:5, 6:6}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff)
{'dic_item_added': ['root[5]', 'root[6]'],
 'dic_item_removed': ['root[4]'],
 'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

Разница строк

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world"}}
>>> t2 = {1:1, 2:4, 3:3, 4:{"a":"hello", "b":"world!"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'values_changed': { 'root[2]': {'newvalue': 4, 'oldvalue': 2},
                      "root[4]['b']": { 'newvalue': 'world!',
                                        'oldvalue': 'world'}}}

Разница строк 2

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world!\nGoodbye!\n1\n2\nEnd"}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n1\n2\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'values_changed': { "root[4]['b']": { 'diff': '--- \n'
                                                '+++ \n'
                                                '@@ -1,5 +1,4 @@\n'
                                                '-world!\n'
                                                '-Goodbye!\n'
                                                '+world\n'
                                                ' 1\n'
                                                ' 2\n'
                                                ' End',
                                        'newvalue': 'world\n1\n2\nEnd',
                                        'oldvalue': 'world!\n'
                                                    'Goodbye!\n'
                                                    '1\n'
                                                    '2\n'
                                                    'End'}}}

>>> 
>>> print (ddiff['values_changed']["root[4]['b']"]["diff"])
--- 
+++ 
@@ -1,5 +1,4 @@
-world!
-Goodbye!
+world
 1
 2
 End

Изменение типа

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n\n\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'type_changes': { "root[4]['b']": { 'newtype': <class 'str'>,
                                      'newvalue': 'world\n\n\nEnd',
                                      'oldtype': <class 'list'>,
                                      'oldvalue': [1, 2, 3]}}}

Разница в списке

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3, 4]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{'iterable_item_removed': {"root[4]['b'][2]": 3, "root[4]['b'][3]": 4}}

Разница в списке 2:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'iterable_item_added': {"root[4]['b'][3]": 3},
  'values_changed': { "root[4]['b'][1]": {'newvalue': 3, 'oldvalue': 2},
                      "root[4]['b'][2]": {'newvalue': 2, 'oldvalue': 3}}}

Разница в списке, игнорирующая порядок или дубликаты: (с теми же словарями, что и выше)

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
>>> ddiff = DeepDiff(t1, t2, ignore_order=True)
>>> print (ddiff)
{}

Список, содержащий словарь:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:1, 2:2}]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:3}]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'dic_item_removed': ["root[4]['b'][2][2]"],
  'values_changed': {"root[4]['b'][2][1]": {'newvalue': 3, 'oldvalue': 1}}}

Наборы:

>>> t1 = {1, 2, 8}
>>> t2 = {1, 2, 3, 5}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (DeepDiff(t1, t2))
{'set_item_added': ['root[3]', 'root[5]'], 'set_item_removed': ['root[8]']}

Именованные кортежи:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> t1 = Point(x=11, y=22)
>>> t2 = Point(x=11, y=23)
>>> pprint (DeepDiff(t1, t2))
{'values_changed': {'root.y': {'newvalue': 23, 'oldvalue': 22}}}

Пользовательские объекты:

>>> class ClassA(object):
...     a = 1
...     def __init__(self, b):
...         self.b = b
... 
>>> t1 = ClassA(1)
>>> t2 = ClassA(2)
>>> 
>>> pprint(DeepDiff(t1, t2))
{'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

Добавлен атрибут объекта:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
Seperman
источник
Спасибо тебе за это! Только что реализованный на моем проекте, прекрасно работает!
Гталарико
1
@gtalarico Рад помочь! Спасибо за добрые слова!
Сеперман
есть ли возможность игнорировать разницу в списке ? потому что мое приложение не заботится об этом.
Лей Ян
Хороший проект, сделал всю работу с минимальными усилиями с моей стороны. Спасибо!
Станислав Цепа
@LeiYang Да, вы можете установить ignore_order=True. Вы можете найти документы по адресу deepdiff.readthedocs.io/en/latest/diff.html
Seperman,
18

не уверен, что это "быстро" или нет, но обычно это можно сделать

dicta = {"a":1,"b":2,"c":3,"d":4}
dictb = {"a":1,"d":2}
for key in dicta.keys():
    if not key in dictb:
        print key
ghostdog74
источник
Вы должны поменяться местами, dictaи dictbтак как он хочет знать, этих ключей dictbнет dicta.
Гамбо
2
for key in dicta.keys():=>for key in dicta:
Жан-Франсуа Фабр
15

Как писал Алекс Мартелли, если вы просто хотите проверить, не находится ли какой-либо ключ в B, не в A, any(True for k in dictB if k not in dictA)был бы путь.

Чтобы найти отсутствующие ключи:

diff = set(dictB)-set(dictA) #sets

C:\Dokumente und Einstellungen\thc>python -m timeit -s "dictA =    
dict(zip(range(1000),range
(1000))); dictB = dict(zip(range(0,2000,2),range(1000)))" "diff=set(dictB)-set(dictA)"
10000 loops, best of 3: 107 usec per loop

diff = [ k for k in dictB if k not in dictA ] #lc

C:\Dokumente und Einstellungen\thc>python -m timeit -s "dictA = 
dict(zip(range(1000),range
(1000))); dictB = dict(zip(range(0,2000,2),range(1000)))" "diff=[ k for k in dictB if
k not in dictA ]"
10000 loops, best of 3: 95.9 usec per loop

Таким образом, эти два решения практически одинаковы.

Йохен Ритцель
источник
8
Это имеет больше смысла:any(k not in dictA for k in dictB)
hughdbrown
13

Если вы действительно имеете в виду именно то, что говорите (то, что вам нужно только выяснить, ЕСЛИ «есть какие-то ключи» в B, а не в A, а НЕ КОГДА ОДНА из них, если таковые имеются), самый быстрый способ должен быть:

if any(True for k in dictB if k not in dictA): ...

Если вам действительно нужно выяснить, КАКИЕ КЛЮЧИ, если они есть, находятся в B, а не в A, и не просто «ЕСЛИ» есть такие ключи, тогда существующие ответы вполне уместны (но я предлагаю большую точность в будущих вопросах, если это так. действительно, что вы имеете в виду ;-).

Алекс Мартелли
источник
8

Используйтеset() :

set(dictA.keys()).intersection(dictB.keys())
Джозил Коста
источник
set(d)уже возвращает только ключи, так что вы можете сделатьset(da).intersection(db)
цифра
8

Главный ответ от hughdbrown предлагает использовать разность множеств, что, безусловно, является лучшим подходом:

diff = set(dictb.keys()) - set(dicta.keys())

Проблема этого кода в том, что он создает два списка только для создания двух наборов, поэтому он тратит 4N времени и 2N пространства. Это также немного сложнее, чем нужно.

Обычно это не имеет большого значения, но если это так:

diff = dictb.keys() - dicta

Python 2

В Python 2 keys()возвращает список ключей, а не a KeysView. Таким образом, вы должны попросить viewkeys()напрямую.

diff = dictb.viewkeys() - dicta

Мы надеемся, sixчто для кода с двумя версиями 2.7 / 3.x вы используете или что-то подобное, поэтому вы можете использовать six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

В 2.4-2.6 нету KeysView. Но вы можете, по крайней мере, сократить стоимость с 4N до N, построив свой левый набор непосредственно из итератора, вместо того, чтобы сначала создавать список:

diff = set(dictb) - dicta

Предметы

У меня есть dictA, которая может быть такой же, как dictB, или может отсутствовать некоторые ключи по сравнению с dictB, иначе значение некоторых ключей может отличаться

Таким образом, вам действительно не нужно сравнивать ключи, но предметы. An ItemsViewявляется только Setесли значения являются хэшируемыми, как строки. Если они есть, это легко:

diff = dictb.items() - dicta.items()

Рекурсивный дифференциал

Хотя вопрос напрямую не задает рекурсивный diff, некоторые из примеров значений являются dicts, и кажется, что ожидаемый результат действительно рекурсивно их дифференцирует. Здесь уже есть несколько ответов, показывающих, как это сделать.

abarnert
источник
окончательный ответ с 2018 года.
Жан-Франсуа Фабр
@ Jean-FrançoisFabre Конечно, материал Python 2.4-2.6 уже довольно неактуален в 2018 году…
abarnert
некоторые люди застряли с 2.6
Жан-Франсуа Фабр
3

Вот способ, который будет работать, учитывает ключи, которые оценивают False, и все еще использует выражение генератора, чтобы выпасть рано, если это возможно. Это не очень красиво, хотя.

any(map(lambda x: True, (k for k in b if k not in a)))

РЕДАКТИРОВАТЬ:

THC4k опубликовал ответ на мой комментарий к другому ответу. Вот лучший способ сделать это:

any(True for k in b if k not in a)

Не уверен, как это никогда не приходило мне в голову ...

Стив Лош
источник
это тот же ответ, что и ранее. Ответ Алексея Мартелли
Жан-Франсуа Фабр
Сейчас. Когда я опубликовал это (девять лет назад, смеется), более ранним ответом было то, any(k for k in dictB if k not in dictA)что это не одно и то же (для фальшивых ключей). Проверьте историю редактирования / метки времени.
Стив Лош
3

Это старый вопрос, и он требует немного меньше, чем мне было нужно, поэтому этот ответ на самом деле решает больше, чем этот вопрос. Ответы на этот вопрос помогли мне решить следующее:

  1. (спросил) Запишите различия между двумя словарями
  2. Объединить различия из № 1 в базовый словарь
  3. (спрашивается) Объединить различия между двумя словарями (трактовать словарь № 2 как словарь различий)
  4. Попытайтесь обнаружить движения предметов, а также изменения
  5. (спросил) Делать все это рекурсивно

Все это в сочетании с JSON обеспечивает довольно мощную поддержку конфигурации хранилища.

Решение ( также на github ):

from collections import OrderedDict
from pprint import pprint


class izipDestinationMatching(object):
    __slots__ = ("attr", "value", "index")

    def __init__(self, attr, value, index):
        self.attr, self.value, self.index = attr, value, index

    def __repr__(self):
        return "izip_destination_matching: found match by '%s' = '%s' @ %d" % (self.attr, self.value, self.index)


def izip_destination(a, b, attrs, addMarker=True):
    """
    Returns zipped lists, but final size is equal to b with (if shorter) a padded with nulls
    Additionally also tries to find item reallocations by searching child dicts (if they are dicts) for attribute, listed in attrs)
    When addMarker == False (patching), final size will be the longer of a, b
    """
    for idx, item in enumerate(b):
        try:
            attr = next((x for x in attrs if x in item), None)  # See if the item has any of the ID attributes
            match, matchIdx = next(((orgItm, idx) for idx, orgItm in enumerate(a) if attr in orgItm and orgItm[attr] == item[attr]), (None, None)) if attr else (None, None)
            if match and matchIdx != idx and addMarker: item[izipDestinationMatching] = izipDestinationMatching(attr, item[attr], matchIdx)
        except:
            match = None
        yield (match if match else a[idx] if len(a) > idx else None), item
    if not addMarker and len(a) > len(b):
        for item in a[len(b) - len(a):]:
            yield item, item


def dictdiff(a, b, searchAttrs=[]):
    """
    returns a dictionary which represents difference from a to b
    the return dict is as short as possible:
      equal items are removed
      added / changed items are listed
      removed items are listed with value=None
    Also processes list values where the resulting list size will match that of b.
    It can also search said list items (that are dicts) for identity values to detect changed positions.
      In case such identity value is found, it is kept so that it can be re-found during the merge phase
    @param a: original dict
    @param b: new dict
    @param searchAttrs: list of strings (keys to search for in sub-dicts)
    @return: dict / list / whatever input is
    """
    if not (isinstance(a, dict) and isinstance(b, dict)):
        if isinstance(a, list) and isinstance(b, list):
            return [dictdiff(v1, v2, searchAttrs) for v1, v2 in izip_destination(a, b, searchAttrs)]
        return b
    res = OrderedDict()
    if izipDestinationMatching in b:
        keepKey = b[izipDestinationMatching].attr
        del b[izipDestinationMatching]
    else:
        keepKey = izipDestinationMatching
    for key in sorted(set(a.keys() + b.keys())):
        v1 = a.get(key, None)
        v2 = b.get(key, None)
        if keepKey == key or v1 != v2: res[key] = dictdiff(v1, v2, searchAttrs)
    if len(res) <= 1: res = dict(res)  # This is only here for pretty print (OrderedDict doesn't pprint nicely)
    return res


def dictmerge(a, b, searchAttrs=[]):
    """
    Returns a dictionary which merges differences recorded in b to base dictionary a
    Also processes list values where the resulting list size will match that of a
    It can also search said list items (that are dicts) for identity values to detect changed positions
    @param a: original dict
    @param b: diff dict to patch into a
    @param searchAttrs: list of strings (keys to search for in sub-dicts)
    @return: dict / list / whatever input is
    """
    if not (isinstance(a, dict) and isinstance(b, dict)):
        if isinstance(a, list) and isinstance(b, list):
            return [dictmerge(v1, v2, searchAttrs) for v1, v2 in izip_destination(a, b, searchAttrs, False)]
        return b
    res = OrderedDict()
    for key in sorted(set(a.keys() + b.keys())):
        v1 = a.get(key, None)
        v2 = b.get(key, None)
        #print "processing", key, v1, v2, key not in b, dictmerge(v1, v2)
        if v2 is not None: res[key] = dictmerge(v1, v2, searchAttrs)
        elif key not in b: res[key] = v1
    if len(res) <= 1: res = dict(res)  # This is only here for pretty print (OrderedDict doesn't pprint nicely)
    return res
въелись
источник
2

как насчет стандарта (сравните FULL Object)

PyDev-> Новый модуль PyDev-> Модуль: unittest

import unittest


class Test(unittest.TestCase):


    def testName(self):
        obj1 = {1:1, 2:2}
        obj2 = {1:1, 2:2}
        self.maxDiff = None # sometimes is usefull
        self.assertDictEqual(d1, d2)

if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']

    unittest.main()
Maxx
источник
Это замечательно, если у вас есть огромный вложенный словарь, и вы хотите сравнить каждую вещь внутри и увидеть различия. Спасибо!
Мэтью Мойзен
2

Если на Python ≥ 2.7:

# update different values in dictB
# I would assume only dictA should be updated,
# but the question specifies otherwise

for k in dictA.viewkeys() & dictB.viewkeys():
    if dictA[k] != dictB[k]:
        dictB[k]= dictA[k]

# add missing keys to dictA

dictA.update( (k,dictB[k]) for k in dictB.viewkeys() - dictA.viewkeys() )
tzot
источник
1

Вот решение для глубокого сравнения двух словарей ключей:

def compareDictKeys(dict1, dict2):
  if type(dict1) != dict or type(dict2) != dict:
      return False

  keys1, keys2 = dict1.keys(), dict2.keys()
  diff = set(keys1) - set(keys2) or set(keys2) - set(keys1)

  if not diff:
      for key in keys1:
          if (type(dict1[key]) == dict or type(dict2[key]) == dict) and not compareDictKeys(dict1[key], dict2[key]):
              diff = True
              break

  return not diff
Рой Бахуми
источник
1

Вот решение, которое может сравнить более двух диктов:

def diff_dict(dicts, default=None):
    diff_dict = {}
    # add 'list()' around 'd.keys()' for python 3 compatibility
    for k in set(sum([d.keys() for d in dicts], [])):
        # we can just use "values = [d.get(k, default) ..." below if 
        # we don't care that d1[k]=default and d2[k]=missing will
        # be treated as equal
        if any(k not in d for d in dicts):
            diff_dict[k] = [d.get(k, default) for d in dicts]
        else:
            values = [d[k] for d in dicts]
            if any(v != values[0] for v in values):
                diff_dict[k] = values
    return diff_dict

пример использования:

import matplotlib.pyplot as plt
diff_dict([plt.rcParams, plt.rcParamsDefault, plt.matplotlib.rcParamsOrig])
tsvikas
источник
1

Мой рецепт симметричной разницы между двумя словарями:

def find_dict_diffs(dict1, dict2):
    unequal_keys = []
    unequal_keys.extend(set(dict1.keys()).symmetric_difference(set(dict2.keys())))
    for k in dict1.keys():
        if dict1.get(k, 'N\A') != dict2.get(k, 'N\A'):
            unequal_keys.append(k)
    if unequal_keys:
        print 'param', 'dict1\t', 'dict2'
        for k in set(unequal_keys):
            print str(k)+'\t'+dict1.get(k, 'N\A')+'\t '+dict2.get(k, 'N\A')
    else:
        print 'Dicts are equal'

dict1 = {1:'a', 2:'b', 3:'c', 4:'d', 5:'e'}
dict2 = {1:'b', 2:'a', 3:'c', 4:'d', 6:'f'}

find_dict_diffs(dict1, dict2)

И результат:

param   dict1   dict2
1       a       b
2       b       a
5       e       N\A
6       N\A     f
smoke_lp
источник
1

Как уже упоминалось в других ответах, unittest выдает неплохой результат для сравнения dicts, но в этом примере нам не нужно сначала создавать целый тест.

Отбирая источник юнит-теста, похоже, что вы можете получить справедливое решение с помощью всего этого:

import difflib
import pprint

def diff_dicts(a, b):
    if a == b:
        return ''
    return '\n'.join(
        difflib.ndiff(pprint.pformat(a, width=30).splitlines(),
                      pprint.pformat(b, width=30).splitlines())
    )

так

dictA = dict(zip(range(7), map(ord, 'python')))
dictB = {0: 112, 1: 'spam', 2: [1,2,3], 3: 104, 4: 111}
print diff_dicts(dictA, dictB)

Результаты в:

{0: 112,
-  1: 121,
-  2: 116,
+  1: 'spam',
+  2: [1, 2, 3],
   3: 104,
-  4: 111,
?        ^

+  4: 111}
?        ^

-  5: 110}

Куда:

  • «-» указывает ключ / значения в первом, но не во втором
  • «+» указывает ключ / значение во втором, но не в первом слове

Как и в unittest, единственное предостережение заключается в том, что окончательное отображение можно считать diff из-за запятой / скобки.

Райан де Клер
источник
1

У @Maxx отличный ответ, используйте unittestинструменты, предоставляемые Python:

import unittest


class Test(unittest.TestCase):
    def runTest(self):
        pass

    def testDict(self, d1, d2, maxDiff=None):
        self.maxDiff = maxDiff
        self.assertDictEqual(d1, d2)

Затем в любом месте вашего кода вы можете позвонить:

try:
    Test().testDict(dict1, dict2)
except Exception, e:
    print e

Результирующий вывод выглядит как вывод из « diffкрасивой печати» словарей с +или -добавляя каждую строку, которая отличается.

Брент Уошберн
источник
0

Не уверен, что это все еще актуально, но я столкнулся с этой проблемой, в моей ситуации мне просто нужно было вернуть словарь изменений для всех вложенных словарей и т. Д. И т. Д. Не удалось найти хорошее решение, но я в итоге написал простую функцию сделать это . Надеюсь это поможет,

Джонатан Микл
источник
2
Было бы предпочтительно иметь наименьшее количество кода, который фактически устраняет проблему ОП в ответе, а не ссылку. Если ссылка умирает или перемещается, ваш ответ становится бесполезным.
Джордж Стокер
0

Если вам нужно встроенное решение для полного сравнения с произвольными структурами dict, ответ @ Maxx - хорошее начало.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)
штифтик
источник
Вы, очевидно, не можете создать такой тестовый класс, что очень плохо.
Бен Лиянэйдж,
0

Основываясь на ответе ghostdog74,

dicta = {"a":1,"d":2}
dictb = {"a":5,"d":2}

for value in dicta.values():
    if not value in dictb.values():
        print value

будет печатать различное значение dicta

normalUser
источник
0

Попробуйте это, чтобы найти де пересечение, ключи, которые есть в обоих словарях, если вы хотите, чтобы ключи не были найдены во втором словаре, просто используйте not in ...

intersect = filter(lambda x, dictB=dictB.keys(): x in dictB, dictA.keys())
Диого Сантьяго
источник