Обчисліть різницю ключів, що містяться у двох словниках 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" слід додати всередину дикту. Ключове значення не має фіксованого формату. Це може бути просте значення або диктат, або диктат диктату.

Відповіді:


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


3
Розумне рішення, дякую! Я змусив це працювати з вкладеними диктами, перевіривши, чи є змінені чи незмінені значення екземплярами диктанту, і викликав рекурсивну функцію, щоб перевірити їх знову за допомогою вашого класу.
AJJ

1
@AJJ Я хотів би бачити цю реалізацію.
urschrei

1
Як щодо того def update(self, new_dict): self.__init__(new_dict, self.current_dict)чи іншого, так що ви можете зробити порівняльне порівняння
Нік Т

Деякі зауваження: DictDifferклас - це клас без громадянства і може бути функцією. Значення changedта unchangedзначення можуть бути обчислені в одному циклі. Ці дві функції можуть повернути listзамість того, setщо, безумовно, дешевше. Для глибокого порівняння ви можете ознайомитися з рамкою тестування блоку: docs.python.org/2/library/unittest.html , просто дотримуйтесь assertDictEqualметоду у вихідному коді.
Лоран ЛАПОРТ

1
FWIW, set(dictb)напевно, краще, ніж set(dictb.keys()).
mgilson

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}}}

Дякую за це! Щойно реалізований на моєму проекті, чудово працює!
gtalarico

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

Ви повинні поміняти місцями, dictaі dictbоскільки він хоче знати ті ключі dictb, яких немає dicta.
Gumbo

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

Верхня відповідь hughdbrown пропонує використовувати різницю множин, яка, безумовно, є найкращим підходом:

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

Проблема цього коду полягає в тому, що він створює два списки просто для створення двох наборів, тому він витрачає 4N часу та 2N місця. Це також трохи складніше, ніж це повинно бути.

Зазвичай це не велика справа, але якщо це:

diff = dictb.keys() - dicta

Пітон 2

У Python 2 keys()повертає список ключів, а не a KeysView. Тож вам потрібно просити viewkeys()прямо.

diff = dictb.viewkeys() - dicta

Для коду подвійної версії 2.7 / 3.x, сподіваємось, ви використовуєте sixщось подібне, тож ви можете використовувати six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

У 2.4-2.6 немає KeysView. Але ви можете принаймні скоротити вартість з 4N до N, побудувавши лівий набір безпосередньо з ітератора, замість того, щоб спочатку складати список:

diff = set(dictb) - dicta

Предмети

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

Тому вам не потрібно порівнювати ключі, а елементи. А ItemsViewє лише a, Setякщо значення є доступними, як рядки. Якщо вони є, це легко:

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

Рекурсивний розл

Хоча питання не запитує прямо рекурсивного розрізнення, деякі приклади значень є диктами, і, схоже, очікуваний вихід рекурсивно їх відрізняє. Тут вже є кілька відповідей, які показують, як це зробити.


остаточна відповідь з 2018 року.
Жан-Франсуа Фабре

@ Jean-FrançoisFabre Звичайно, речі в Python
2.4-2.6

у деяких людей застрягло 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

як щодо стандарту (порівняйте ПОВНИЙ об’єкт)

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()

Це чудово, якщо у вас є величезний вкладений словник і ви хочете порівняти кожну річ всередині і побачити відмінності. Дякую!
Меттью Мойсен

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() )

1

Ось рішення для глибокого порівняння 2 клавіш словників:

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

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

1

Як вже згадувалося в інших відповідях, unittest видає хороший вихід для порівняння диктів, але в цьому прикладі ми не хочемо спочатку будувати цілий тест.

Очищаючи джерело одиничного тесту, схоже, що ви можете отримати справедливе рішення саме з цим:

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, єдиний застереження полягає в тому, що остаточне відображення може вважатися різним, що обумовлено кінцевою комою / дужкою.


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

буде надрукувати різні значення дикти


0

Спробуйте це, щоб знайти перехрестя, ключі, що знаходяться в обох дикторіях, якщо ви хочете, щоб ключі не були знайдені на другому диктанті, просто використовуйте не в ...

intersect = filter(lambda x, dictB=dictB.keys(): x in dictB, dictA.keys())
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.