Berechnen Sie die Differenz der Schlüssel in zwei Python-Wörterbüchern

171

Angenommen, ich habe zwei Python-Wörterbücher - dictAund dictB. Ich muss herausfinden, ob es Schlüssel gibt, die in, dictBaber nicht in vorhanden sind dictA. Was ist der schnellste Weg?

Soll ich die Wörterbuchschlüssel in ein Set konvertieren und dann loslegen?

Interessiert daran, deine Gedanken zu kennen ...


Vielen Dank für Ihre Antworten.

Entschuldigung, dass ich meine Frage nicht richtig gestellt habe. Mein Szenario ist wie folgt: Ich habe einen, dictAder dictBmit einigen Schlüsseln identisch sein kann oder bei dem einige Schlüssel fehlen, dictBoder der Wert einiger Schlüssel kann unterschiedlich sein, was auf den Wert des Schlüssels eingestellt werden muss dictA.

Das Problem ist, dass das Wörterbuch keinen Standard hat und Werte haben kann, die diktiert werden können.

Sagen

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

Der Wert für 'key2' muss also auf den neuen Wert zurückgesetzt und 'key13' innerhalb des Diktats hinzugefügt werden. Der Schlüsselwert hat kein festes Format. Es kann ein einfacher Wert oder ein Diktat oder ein Diktat des Diktats sein.

Nathan Davis
quelle

Antworten:

234

Sie können Set-Operationen für die Tasten verwenden:

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

Hier ist eine Klasse, um alle Möglichkeiten zu finden: Was wurde hinzugefügt, was wurde entfernt, welche Schlüssel-Wert-Paare sind gleich und welche Schlüssel-Wert-Paare werden geändert.

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

Hier ist eine Beispielausgabe:

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

Verfügbar als Github-Repo: https://github.com/hughdbrown/dictdiffer

hughdbrown
quelle
3
Intelligente Lösung, danke! Ich habe dafür gesorgt, dass es mit verschachtelten Dicts funktioniert, indem ich überprüft habe, ob geänderte oder unveränderte Werte Diktatinstanzen sind, und eine rekursive Funktion aufgerufen habe, um sie mithilfe Ihrer Klasse erneut zu überprüfen.
AJJ
1
@AJJ Ich würde diese Implementierung gerne sehen.
Urschrei
1
Wie wäre es mit einem def update(self, new_dict): self.__init__(new_dict, self.current_dict)oder ähnlichem, damit Sie einen rollierenden Vergleich durchführen können
Nick T
Einige Anmerkungen: Die DictDifferKlasse ist eine zustandslose Klasse und könnte eine Funktion sein. Die changedund unchanged-Werte können in derselben Schleife berechnet werden. Diese beiden Funktionen könnten a listanstelle von a zurückgeben, setwas sicherlich günstiger ist. Für einen umfassenden Vergleich können Sie sich das Unit-Test-Framework ansehen : docs.python.org/2/library/unittest.html. Befolgen Sie einfach die assertDictEqualMethode im Quellcode.
Laurent LAPORTE
1
FWIW set(dictb)ist wahrscheinlich besser als set(dictb.keys()).
mgilson
60

Für den Fall, dass Sie den Unterschied rekursiv möchten, habe ich ein Paket für Python geschrieben: https://github.com/seperman/deepdiff

Installation

Von PyPi installieren:

pip install deepdiff

Anwendungsbeispiel

Importieren

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

Das gleiche Objekt wird leer zurückgegeben

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

Der Typ eines Elements hat sich geändert

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

Der Wert eines Artikels hat sich geändert

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

Artikel hinzugefügt und / oder entfernt

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

String Unterschied

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

Saitendifferenz 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

Typänderung

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

Listenunterschied

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

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

Listenunterschiede ohne Berücksichtigung von Reihenfolge oder Duplikaten: (mit denselben Wörterbüchern wie oben)

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

Liste mit Wörterbuch:

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

Sets:

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

Benannte Tupel:

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

Benutzerdefinierte Objekte:

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

Objektattribut hinzugefügt:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
Seperman
quelle
Danke dafür! Gerade in meinem Projekt implementiert, funktioniert super!
Gtalarico
1
@gtalarico Gerne helfen wir! Danke für die netten Worte!
Seperman
Gibt es eine Option, um den Unterschied in der Listenreihenfolge zu ignorieren ? weil meine Bewerbung sich nicht darum kümmert.
Lei Yang
Schönes Projekt, habe alle Arbeit mit minimalem Aufwand von meiner Seite erledigt. Vielen Dank!
Stanislav Tsepa
@LeiYang Ja du kannst einstellen ignore_order=True. Sie finden die Dokumente unter deepdiff.readthedocs.io/en/latest/diff.html
Seperman
18

Ich bin mir nicht sicher, ob es "schnell" ist oder nicht, aber normalerweise kann man das tun

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
quelle
Sie müssen tauschen dictaund dictbda er wissen will, sind die Schlüssel davon dictbnicht in dicta.
Gumbo
2
for key in dicta.keys():=>for key in dicta:
Jean-François Fabre
15

Wie Alex Martelli schrieb, wenn Sie einfach überprüfen möchten, ob ein Schlüssel in B nicht in A ist, any(True for k in dictB if k not in dictA) wäre es der richtige Weg .

So finden Sie die fehlenden Schlüssel:

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

Diese beiden Lösungen sind also ziemlich gleich schnell.

Jochen Ritzel
quelle
8
Dies macht mehr Sinn:any(k not in dictA for k in dictB)
Hughdbrown
13

Wenn Sie wirklich genau das meinen, was Sie sagen (dass Sie nur herausfinden müssen, WENN "es Schlüssel gibt" in B und nicht in A, nicht WELCHE sind diese, wenn überhaupt), sollte der schnellste Weg sein:

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

Wenn Sie tatsächlich herausfinden müssen, WELCHE SCHLÜSSEL, falls vorhanden, in B und nicht in A sind und nicht nur "WENN", gibt es solche Schlüssel, dann sind vorhandene Antworten durchaus angemessen (aber ich schlage in zukünftigen Fragen mehr Präzision vor, wenn dies der Fall ist in der Tat was du meinst ;-).

Alex Martelli
quelle
8

Verwendungset() :

set(dictA.keys()).intersection(dictB.keys())
Joziel Costa
quelle
set(d)gibt bereits nur die Schlüssel zurück, so dass Sie tun könnenset(da).intersection(db)
Ziffer
8

Die Top-Antwort von hughdbrown schlägt vor, einen Satzunterschied zu verwenden, was definitiv der beste Ansatz ist:

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

Das Problem mit diesem Code ist, dass er zwei Listen erstellt, um nur zwei Sätze zu erstellen, sodass 4N Zeit und 2N Speicherplatz verschwendet werden. Es ist auch etwas komplizierter als es sein muss.

Normalerweise ist dies keine große Sache, aber wenn es so ist:

diff = dictb.keys() - dicta

Python 2

Gibt in Python 2 keys()eine Liste der Schlüssel zurück, nicht a KeysView. Sie müssen also viewkeys()direkt danach fragen .

diff = dictb.viewkeys() - dicta

Für Dual-Version 2.7 / 3.x-Code verwenden Sie hoffentlich sixoder etwas Ähnliches, sodass Sie Folgendes verwenden können six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

In 2.4-2.6 gibt es keine KeysView. Sie können jedoch zumindest die Kosten von 4N auf N senken, indem Sie Ihren linken Satz direkt aus einem Iterator heraus erstellen, anstatt zuerst eine Liste zu erstellen:

diff = set(dictb) - dicta

Artikel

Ich habe ein dictA, das mit dictB identisch sein kann oder bei dem einige Schlüssel im Vergleich zu dictB fehlen, oder der Wert einiger Schlüssel kann unterschiedlich sein

Sie müssen also wirklich nicht die Schlüssel vergleichen, sondern die Elemente. An ItemsViewist nur einSet wenn die Werte wie Zeichenfolgen hashbar sind. Wenn ja, ist es einfach:

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

Rekursiver Diff

Obwohl die Frage nicht direkt nach einem rekursiven Diff fragt, sind einige der Beispielwerte Dikte, und es scheint, dass die erwartete Ausgabe sie rekursiv diffundiert. Hier gibt es bereits mehrere Antworten, die zeigen, wie das geht.

abarnert
quelle
die endgültige Antwort von 2018.
Jean-François Fabre
@ Jean-FrançoisFabre Natürlich ist das Python 2.4-2.6-Zeug schon 2018 ziemlich irrelevant…
abarnert
Einige Leute stecken mit 2.6 fest
Jean-François Fabre
3

Hier ist eine Methode, die funktioniert, Schlüssel zulässt, die ausgewertet werden False, und dennoch einen Generatorausdruck verwendet , um nach Möglichkeit frühzeitig auszufallen. Es ist jedoch nicht außergewöhnlich hübsch.

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

BEARBEITEN:

THC4k hat eine Antwort auf meinen Kommentar zu einer anderen Antwort gepostet. Hier ist eine bessere und schönere Möglichkeit, Folgendes zu tun:

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

Ich bin mir nicht sicher, wie mir das nie in den Sinn gekommen ist ...

Steve Losh
quelle
Dies ist die gleiche Antwort wie die frühere Antwort von Alex Martelli
Jean-François Fabre
Ist das jetzt. Als ich es veröffentlichte (vor neun Jahren, lol), war die frühere Antwort, any(k for k in dictB if k not in dictA)die nicht dasselbe ist (für Falsey Keys). Überprüfen Sie den Bearbeitungsverlauf / die Zeitstempel.
Steve Losh
3

Dies ist eine alte Frage und stellt etwas weniger als das, was ich brauchte, sodass diese Antwort tatsächlich mehr löst als diese Frage. Die Antworten in dieser Frage haben mir geholfen, Folgendes zu lösen:

  1. (gefragt) Notieren Sie Unterschiede zwischen zwei Wörterbüchern
  2. Führen Sie die Unterschiede von Nummer 1 zum Basiswörterbuch zusammen
  3. (gefragt) Unterschiede zwischen zwei Wörterbüchern zusammenführen (Wörterbuch Nr. 2 so behandeln, als wäre es ein Diff-Wörterbuch)
  4. Versuchen Sie, Bewegungen und Änderungen von Gegenständen zu erkennen
  5. (gefragt) Mach das alles rekursiv

All dies in Kombination mit JSON bietet eine ziemlich leistungsstarke Unterstützung für Konfigurationsspeicher.

Die Lösung ( auch auf 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
velis
quelle
2

Was ist mit Standard (vergleiche FULL Object)?

PyDev-> neues PyDev-Modul-> Modul: 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
quelle
Dies ist wunderbar, wenn Sie ein riesiges verschachteltes Wörterbuch haben und alles darin vergleichen und die Unterschiede sehen möchten. Vielen Dank!
Matthew Moisen
2

Wenn auf 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
quelle
1

Hier ist eine Lösung für den tiefen Vergleich von 2 Wörterbuchschlüsseln:

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
Roei Bahumi
quelle
1

Hier ist eine Lösung, die mehr als zwei Diktate vergleichen kann:

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

Anwendungsbeispiel:

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

Mein Rezept des symmetrischen Unterschieds zwischen zwei Wörterbüchern:

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)

Und das Ergebnis ist:

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

Wie in anderen Antworten erwähnt, liefert unittest eine nette Ausgabe zum Vergleichen von Diktaten, aber in diesem Beispiel möchten wir nicht zuerst einen ganzen Test erstellen müssen.

Wenn Sie die unitteste Quelle herauskratzen, können Sie mit genau dem eine faire Lösung finden:

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

so

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)

Ergebnisse in:

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

+  4: 111}
?        ^

-  5: 110}

Wo:

  • '-' gibt Schlüssel / Werte im ersten, aber nicht im zweiten Diktat an
  • '+' zeigt Schlüssel / Werte im zweiten, aber nicht im ersten Diktat an

Wie bei unittest besteht die einzige Einschränkung darin, dass die endgültige Zuordnung aufgrund des nachgestellten Kommas / der Klammer als Diff angesehen werden kann.

Ryan de Kleer
quelle
1

@Maxx hat eine ausgezeichnete Antwort. Verwenden Sie die unittestvon Python bereitgestellten Tools:

import unittest


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

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

Dann können Sie überall in Ihrem Code anrufen:

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

Die resultierende Ausgabe sieht aus wie die Ausgabe von diff, indem die Wörterbücher mit jeder Zeile, die anders ist , hübsch gedruckt +oder -vorangestellt werden.

Brent Washburne
quelle
0

Ich bin mir nicht sicher, ob es immer noch relevant ist, aber ich bin auf dieses Problem gestoßen. In meiner Situation musste ich nur ein Wörterbuch mit den Änderungen für alle verschachtelten Wörterbücher usw. zurückgeben. Es konnte keine gute Lösung gefunden werden, aber am Ende habe ich eine einfache Funktion geschrieben um dies zu tun . Hoffe das hilft,

Jonathan Mickle
quelle
2
Es wäre vorzuziehen, anstelle eines Links die kleinste Menge an Code in der Antwort zu haben, die das Problem des OP tatsächlich behebt. Wenn der Link stirbt oder sich bewegt, wird Ihre Antwort unbrauchbar.
George Stocker
0

Wenn Sie eine integrierte Lösung für einen vollständigen Vergleich mit beliebigen Diktatstrukturen wünschen, ist die Antwort von @ Maxx ein guter Anfang.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)
Brad
quelle
Sie können anscheinend eine solche Testklasse nicht instanziieren, was zu schade ist.
Ben Liyanage
0

Basierend auf der Antwort von ghostdog74,

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

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

druckt einen anderen Wert von dicta aus

normalUser
quelle
0

Versuchen Sie dies, um die Kreuzung zu finden, die Schlüssel, die sich in beiden Wörterbüchern befinden. Wenn Sie möchten, dass die Schlüssel im zweiten Wörterbuch nicht gefunden werden, verwenden Sie einfach die nicht in ...

intersect = filter(lambda x, dictB=dictB.keys(): x in dictB, dictA.keys())
Diogo Santiago
quelle