Oblicz różnicę w kluczach zawartych w dwóch słownikach Pythona


171

Załóżmy, że mam dwa słowniki Pythona - dictAi dictB. Muszę się dowiedzieć, czy są jakieś klucze, które są w środku, dictBale ich nie ma dictA. Jak najszybciej można to zrobić?

Czy powinienem przekonwertować klucze słownika na zestaw, a następnie przejść?

Chcesz poznać swoje myśli ...


Dziękuję za odpowiedzi.

Przepraszam za niepoprawne sformułowanie mojego pytania. Mój scenariusz jest taki - mam, dictAktóry może być taki sam, jak dictBlub może brakować niektórych kluczy w porównaniu do dictBlub wartość niektórych kluczy może być inna, co musi być ustawione dictAna wartość klucza.

Problem polega na tym, że słownik nie ma standardu i może mieć wartości, które można dyktować.

Mówić

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

Tak więc wartość „klucz2” musi zostać zresetowana do nowej wartości, a „klucz13” musi zostać dodana wewnątrz dykta. Wartość klucza nie ma ustalonego formatu. Może to być prosta wartość lub dyktando lub dyktando.

Odpowiedzi:


234

Możesz użyć operacji set na klawiszach:

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

Oto klasa, w której można znaleźć wszystkie możliwości: co zostało dodane, co zostało usunięte, które pary klucz-wartość są takie same i które pary klucz-wartość zostały zmienione.

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

Oto przykładowe dane wyjściowe:

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

Dostępne jako repozytorium github: https://github.com/hughdbrown/dictdiffer


3
Sprytne rozwiązanie, dzięki! Sprawiłem, że działało z zagnieżdżonymi dyktami, sprawdzając, czy zmienione lub niezmienione wartości są instancjami dict i wywołując funkcję rekurencyjną, aby ponownie sprawdzić je za pomocą Twojej klasy.
AJJ

1
@AJJ Bardzo chciałbym zobaczyć tę implementację.
urschrei

1
Co powiesz na a def update(self, new_dict): self.__init__(new_dict, self.current_dict)lub coś podobnego, żebyś mógł zrobić kroczące porównanie
Nick T

Kilka uwag: DictDifferklasa jest klasą bezstanową i może być funkcją. Wartości changedi unchangedmożna obliczyć w tej samej pętli. Te dwie funkcje mogą zwrócić listzamiast a, setco jest z pewnością tańsze. Aby uzyskać szczegółowe porównanie, możesz przyjrzeć się strukturze testów jednostkowych: docs.python.org/2/library/unittest.html , po prostu postępuj zgodnie z assertDictEqualmetodą w kodzie źródłowym.
Laurent LAPORTE

1
FWIW, set(dictb)jest prawdopodobnie lepszy niż set(dictb.keys()).
mgilson

60

Jeśli chcesz, aby różnica była rekurencyjna, napisałem pakiet dla Pythona: https://github.com/seperman/deepdiff

Instalacja

Zainstaluj z PyPi:

pip install deepdiff

Przykładowe użycie

Importowanie

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

Ten sam obiekt zwraca pusty

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

Typ elementu się zmienił

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

Wartość pozycji uległa zmianie

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

Dodano i / lub usunięto element

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

Różnica strun

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

Różnica strun 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

Zmiana typu

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

Lista różnic

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

Różnica w liście 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}}}

Wypisz różnicę ignorując kolejność lub duplikaty: (z tymi samymi słownikami co powyżej)

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

Lista zawierająca słownik:

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

Zestawy:

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

Nazwane krotki:

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

Obiekty niestandardowe:

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

Dodano atrybut obiektu:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

Dziękuję Ci za to! Właśnie wdrożony w moim projekcie, działa świetnie!
gtalarico

1
@gtalarico Chętnie pomogę! Dzięki za miłe słowa!
Seperman

czy jest jakaś opcja ignorowania różnicy w kolejności na liście ? ponieważ moja aplikacja nie dba o to.
Lei Yang

Niezły projekt, wykonałem całą pracę przy minimalnym wysiłku z mojej strony. Dzięki!
Stanislav Tsepa

@LeiYang Tak, możesz ustawić ignore_order=True. Można znaleźć w dokumentacji na deepdiff.readthedocs.io/en/latest/diff.html
Seperman

18

nie jestem pewien, czy jest to „szybkie”, czy nie, ale normalnie można to zrobić

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

Musisz się zamienić, dictaa dictbponieważ chce wiedzieć, których kluczy dictbnie ma dicta.
Gumbo

2
for key in dicta.keys():=>for key in dicta:
Jean-François Fabre

15

Jak napisał Alex Martelli, jeśli chcesz po prostu sprawdzić, czy któryś klawisz w B nie znajduje się w A, any(True for k in dictB if k not in dictA) będzie dobrym rozwiązaniem.

Aby znaleźć brakujące klucze:

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

Więc te dwa rozwiązania mają prawie taką samą prędkość.


8
Ma to większy sens:any(k not in dictA for k in dictB)
hughdbrown

13

Jeśli naprawdę masz na myśli dokładnie to, co mówisz (że musisz tylko dowiedzieć się, JEŻELI „są jakieś klucze” w B, a nie w A, a nie KTÓRYCH mogą to być, jeśli w ogóle), najszybszym sposobem powinno być:

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

Jeśli naprawdę chcesz dowiedzieć się, które KLUCZE, jeśli w ogóle, znajdują się w B, a nie w A, a nie tylko „JEŚLI” są takie klucze, to istniejące odpowiedzi są całkiem odpowiednie (ale sugeruję większą precyzję w przyszłych pytaniach, jeśli tak rzeczywiście co masz na myśli ;-).



8

Najlepsza odpowiedź autorstwa hughdbrown sugeruje użycie różnicy zestawów, co jest zdecydowanie najlepszym podejściem:

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

Problem z tym kodem polega na tym, że buduje dwie listy tylko po to, aby utworzyć dwa zestawy, więc marnuje 4N czasu i 2N miejsca. Jest to też trochę bardziej skomplikowane, niż powinno.

Zwykle nie jest to wielka sprawa, ale jeśli tak jest:

diff = dictb.keys() - dicta

Python 2

W Pythonie 2 keys()zwraca listę kluczy, a nie plik KeysView. Więc musisz zapytać viewkeys()bezpośrednio.

diff = dictb.viewkeys() - dicta

W przypadku kodu z podwójną wersją 2.7 / 3.x, miejmy nadzieję, używasz sixczegoś podobnego, więc możesz użyć six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

W 2.4-2.6 nie ma KeysView. Ale możesz przynajmniej obniżyć koszt z 4N do N, budując swój lewy zestaw bezpośrednio z iteratora, zamiast najpierw budować listę:

diff = set(dictb) - dicta

Przedmiotów

Mam dictA, który może być taki sam jak dictB lub może brakować niektórych kluczy w porównaniu z dictB lub wartość niektórych kluczy może być inna

Więc naprawdę nie musisz porównywać kluczy, ale przedmioty. A ItemsViewjest tylko Setwtedy, gdy wartości są hashowane, jak ciągi. Jeśli tak, to proste:

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

Różnica rekurencyjna

Chociaż pytanie nie dotyczy bezpośrednio rekurencyjnej różnicy, niektóre z przykładowych wartości są dyktami i wydaje się, że oczekiwany wynik różni je rekurencyjnie. Istnieje już wiele odpowiedzi pokazujących, jak to zrobić.


zdecydowana odpowiedź z 2018 roku.
Jean-François Fabre

@ Jean-FrançoisFabre Oczywiście rzeczy w Pythonie 2.4-2.6 są już raczej nieistotne w 2018…
abarnert

niektórzy utknęli z 2.6
Jean-François Fabre


3

Oto sposób, który zadziała, zezwala na klucze, które oceniają Falsei nadal używają wyrażenia generatora, aby wypadać wcześnie, jeśli to możliwe. Nie jest jednak wyjątkowo ładny.

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

EDYTOWAĆ:

THC4k opublikował odpowiedź na mój komentarz dotyczący innej odpowiedzi. Oto lepszy, ładniejszy sposób wykonania powyższego:

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

Nie jestem pewien, jak to nigdy nie przyszło mi do głowy ...


jest to ta sama odpowiedź, co wcześniejsza odpowiedź Alexa Martelli
Jean-François Fabre

To jest teraz. Kiedy go opublikowałem (dziewięć lat temu, lol), wcześniejsza odpowiedź brzmiała, any(k for k in dictB if k not in dictA)co nie jest tym samym (dla kluczy falsey). Sprawdź historię zmian / znaczniki czasu.
Steve Losh

3

To jest stare pytanie i wymaga trochę mniej niż to, czego potrzebowałem, więc ta odpowiedź w rzeczywistości rozwiązuje więcej niż to pytanie. Odpowiedzi na to pytanie pomogły mi rozwiązać następujące kwestie:

  1. (zapytany) Zapisz różnice między dwoma słownikami
  2. Scal różnice z punktu 1 w słowniku podstawowym
  3. (zapytane) Połącz różnice między dwoma słownikami (traktuj słownik nr 2 tak, jakby był słownikiem różnic)
  4. Spróbuj wykryć ruchy przedmiotów, a także zmiany
  5. (zapytany) Rób to wszystko rekurencyjnie

Wszystko to w połączeniu z JSON zapewnia dość potężną obsługę magazynu konfiguracji.

Rozwiązanie ( również na githubie ):

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

co ze standardem (porównaj FULL Object)

PyDev-> nowy moduł PyDev-> Moduł: 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()

Jest to wspaniałe, jeśli masz ogromny słownik zagnieżdżony i chcesz porównać wszystko w środku i zobaczyć różnice. Dzięki!
Matthew Moisen

2

Jeśli na Pythonie ≥ 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

Oto rozwiązanie do głębokiego porównania 2 kluczy słowników:

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

oto rozwiązanie, które może porównać więcej niż dwa dykty:

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

przykład użycia:

import matplotlib.pyplot as plt
diff_dict([plt.rcParams, plt.rcParamsDefault, plt.matplotlib.rcParamsOrig])

1

Mój przepis na symetryczną różnicę między dwoma słownikami:

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)

A wynik to:

param   dict1   dict2
1       a       b
2       b       a
5       e       N\A
6       N\A     f

1

Jak wspomniano w innych odpowiedziach, unittest generuje niezłe dane wyjściowe do porównywania dykt, ale w tym przykładzie nie chcemy najpierw tworzyć całego testu.

Po skrobaniu najmniejszego źródła wygląda na to, że możesz uzyskać uczciwe rozwiązanie za pomocą tego:

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

więc

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)

Prowadzi do:

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

+  4: 111}
?        ^

-  5: 110}

Gdzie:

  • „-” wskazuje klucz / wartości w pierwszym, ale nie drugim dyktandzie
  • „+” wskazuje klucz / wartości w drugim, ale nie pierwszym dyktandzie

Podobnie jak w unittest, jedynym zastrzeżeniem jest to, że końcowe odwzorowanie można uznać za różnicę, ze względu na końcowy przecinek / nawias.


1

@Maxx ma doskonałą odpowiedź, skorzystaj z unittestnarzędzi dostarczonych przez Pythona:

import unittest


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

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

Następnie w dowolnym miejscu kodu możesz zadzwonić:

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

Wynikowy wynik wygląda jak wyjście z diff, ładnie wypisując słowniki z +lub -poprzedzając każdą inną linię.


0

Nie jestem pewien, czy to nadal jest istotne, ale natknąłem się na ten problem, w mojej sytuacji wystarczyło zwrócić słownik zmian dla wszystkich zagnieżdżonych słowników itp. Itp. Nie mogłem znaleźć dobrego rozwiązania, ale ostatecznie napisałem prostą funkcję zrobić to . Mam nadzieję że to pomoże,


2
Lepiej byłoby mieć najmniejszą ilość kodu, który naprawia problem OP w odpowiedzi, zamiast łącza. Jeśli link zniknie lub zostanie przeniesiony, Twoja odpowiedź stanie się bezużyteczna.
George Stocker

0

Jeśli chcesz mieć wbudowane rozwiązanie do pełnego porównania z dowolnymi strukturami dyktowania, odpowiedź @ Maxx jest dobrym początkiem.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)

Najwyraźniej nie możesz utworzyć instancji takiej klasy testowej, co jest bardzo złe.
Ben Liyanage

0

Na podstawie odpowiedzi ghostdog74,

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

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

wypisze inną wartość dicta


0

Spróbuj tego, aby znaleźć przecięcie, klucze, które są w obu słownikach, jeśli chcesz, aby klucze nie znalazły się w drugim słowniku, po prostu użyj nie w ...

intersect = filter(lambda x, dictB=dictB.keys(): x in dictB, dictA.keys())
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.