it-swarm.com.de

Zwei Wörterbücher in Python vergleichen

Ich habe zwei Wörterbücher, aber zur Vereinfachung nehme ich diese zwei:

>>> x = dict(a=1, b=2)
>>> y = dict(a=2, b=2)

Nun möchte ich vergleichen, ob jedes key, value-Paar in x den gleichen entsprechenden Wert in y hat. Also schrieb ich folgendes:

>>> for x_values, y_values in Zip(x.iteritems(), y.iteritems()):
        if x_values == y_values:
            print 'Ok', x_values, y_values
        else:
            print 'Not', x_values, y_values

Und es funktioniert, da eine Tuple zurückgegeben wird und dann auf Gleichheit verglichen wird.

Meine Fragen:

Ist das richtig? Gibt es einen besseren Weg, dies zu tun? Besser nicht in Geschwindigkeit, ich spreche von Code-Eleganz.

UPDATE: Ich habe vergessen zu erwähnen, dass ich überprüfen muss, wie viele key, value-Paare gleich sind. 

180
user225312

Wenn Sie wissen möchten, wie viele Werte in beiden Wörterbüchern übereinstimmen, sollten Sie das gesagt haben :) 

Vielleicht so etwas:

shared_items = {k: x[k] for k in x if k in y and x[k] == y[k]}
print len(shared_items)
132
mouad

Was Sie tun möchten, ist einfach x==y 

Was Sie tun, ist keine gute Idee, da die Elemente in einem Wörterbuch keine Reihenfolge haben sollen. Sie können [('a',1),('b',1)] mit [('b',1), ('a',1)] vergleichen (gleiche Wörterbücher, andere Reihenfolge).

Siehe zum Beispiel:

>>> x = dict(a=2, b=2,c=3, d=4)
>>> x
{'a': 2, 'c': 3, 'b': 2, 'd': 4}
>>> y = dict(b=2,c=3, d=4)
>>> y
{'c': 3, 'b': 2, 'd': 4}
>>> Zip(x.iteritems(), y.iteritems())
[(('a', 2), ('c', 3)), (('c', 3), ('b', 2)), (('b', 2), ('d', 4))]

Der Unterschied ist nur ein Element, aber Ihr Algorithmus wird feststellen, dass alle Elemente unterschiedlich sind

149
Jochen Ritzel
def dict_compare(d1, d2):
    d1_keys = set(d1.keys())
    d2_keys = set(d2.keys())
    intersect_keys = d1_keys.intersection(d2_keys)
    added = d1_keys - d2_keys
    removed = d2_keys - d1_keys
    modified = {o : (d1[o], d2[o]) for o in intersect_keys if d1[o] != d2[o]}
    same = set(o for o in intersect_keys if d1[o] == d2[o])
    return added, removed, modified, same

x = dict(a=1, b=2)
y = dict(a=2, b=2)
added, removed, modified, same = dict_compare(x, y)
126
Daniel Myers

dic1 == dic2

Aus Python-Dokumenten

Zur Veranschaulichung geben die folgenden Beispiele alle ein Wörterbuch gleich an .__ zurück. {"one": 1, "two": 2, "three": 3}:

>>> a = dict(one=1, two=2, three=3)
>>> b = {'one': 1, 'two': 2, 'three': 3}
>>> c = dict(Zip(['one', 'two', 'three'], [1, 2, 3]))
>>> d = dict([('two', 2), ('one', 1), ('three', 3)])
>>> e = dict({'three': 3, 'one': 1, 'two': 2})
>>> a == b == c == d == e
True

Gültig für py2 und py3.

56
Pedro Lobito

Ich bin neu im Python, aber am Ende habe ich etwas Ähnliches wie @mouad gemacht

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

Der Operator XOR (^) sollte alle Elemente des Diktats entfernen, wenn sie in beiden Diktaten gleich sind.

50
philipp

Benutz einfach:

assert cmp(dict1, dict2) == 0
42
Shiyu

Da es so scheint, als ob niemand deepdiff erwähnt hat, werde ich es hier nur der Vollständigkeit halber hinzufügen .. _.

import deepdiff
from pprint import pprint

aa = {
    "a": 1,
    "nested": {
        "b": 1,
    }
}
bb = {
    "a": 2,
    "nested": {
        "b": 2,
    }
}
pprint(deepdiff.DeepDiff(aa, bb))

Ausgabe:

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

HINWEIS:

  • Das Paket deepdiff muss installiert werden, da dies kein Standardpaket ist

  • für das Parsen des Ergebnisses ist ein gewisser Aufwand erforderlich


Um den Unterschied der Wörterbücher zu nehmen, finde ich dictdiffer jedoch sehr praktisch.

14
Anubis

@mouads Antwort ist Nizza, wenn Sie davon ausgehen, dass beide Wörterbücher nur einfache Werte enthalten. Bei Wörterbüchern, die Wörterbücher enthalten, wird jedoch eine Ausnahme angezeigt, da Wörterbücher nicht hashierbar sind.

Von meinem Kopf aus könnte so etwas funktionieren:

def compare_dictionaries(dict1, dict2):
     if dict1 is None or dict2 is None:
        print('Nones')
        return False

     if (not isinstance(dict1, dict)) or (not isinstance(dict2, dict)):
        print('Not dict')
        return False

     shared_keys = set(dict2.keys()) & set(dict2.keys())

     if not ( len(shared_keys) == len(dict1.keys()) and len(shared_keys) == len(dict2.keys())):
        print('Not all keys are shared')
        return False


     dicts_are_equal = True
     for key in dict1.keys():
         if isinstance(dict1[key], dict) or isinstance(dict2[key], dict):
             dicts_are_equal = dicts_are_equal and compare_dictionaries(dict1[key], dict2[key])
         else:
             dicts_are_equal = dicts_are_equal and all(atleast_1d(dict1[key] == dict2[key]))

     return dicts_are_equal
7
Alexander

Eine weitere Möglichkeit, bis zur letzten Note des OP, besteht darin, die Hashes (SHA oder MD) der als JSON abgelegten Dicts zu vergleichen. Die Art und Weise, wie Hashes konstruiert werden, garantiert, dass die Quell-Strings gleich sind, wenn sie gleich sind. Das ist sehr schnell und mathematisch einwandfrei. 

import json
import hashlib

def hash_dict(d):
    return hashlib.sha1(json.dumps(d, sort_keys=True)).hexdigest()

x = dict(a=1, b=2)
y = dict(a=2, b=2)
z = dict(a=1, b=2)

print(hash_dict(x) == hash_dict(y))
print(hash_dict(x) == hash_dict(z))
5
WoJ

So testen Sie, ob zwei Schlüsselwörter und Werte gleich sind:

def dicts_equal(d1,d2):
    """ return True if all keys and values are the same """
    return all(k in d2 and d1[k] == d2[k]
               for k in d1) \
        and all(k in d1 and d1[k] == d2[k]
               for k in d2)

Wenn Sie die unterschiedlichen Werte zurückgeben möchten, schreiben Sie es anders:

def dict1_minus_d2(d1, d2):
    """ return the subset of d1 where the keys don't exist in d2 or
        the values in d2 are different, as a dict """
    return {k,v for k,v in d1.items() if k in d2 and v == d2[k]}

Sie müssten es zweimal anrufen 

dict1_minus_d2(d1,d2).extend(dict1_minus_d2(d2,d1))
3
simonltwick

Die Funktion ist gut IMO, klar und intuitiv. Aber nur um Ihnen eine (andere) Antwort zu geben, hier ist mein Weg:

def compare_dict(dict1, dict2):
    for x1 in dict1.keys():
        z = dict1.get(x1) == dict2.get(x1)
        if not z:
            print('key', x1)
            print('value A', dict1.get(x1), '\nvalue B', dict2.get(x1))
            print('-----\n')

Kann für Sie oder für andere Personen nützlich sein ..

2
zwep

Code

def equal(a, b):
    type_a = type(a)
    type_b = type(b)

    if type_a != type_b:
        return False

    if isinstance(a, dict):
        if len(a) != len(b):
            return False
        for key in a:
            if key not in b:
                return False
            if not equal(a[key], b[key]):
                return False
        return True

    Elif isinstance(a, list):
        if len(a) != len(b):
            return False
        while len(a):
            x = a.pop()
            index = indexof(x, b)
            if index == -1:
                return False
            del b[index]
        return True

    else:
        return a == b

def indexof(x, a):
    for i in range(len(a)):
        if equal(x, a[i]):
            return i
    return -1

Prüfung

>>> a = {
    'number': 1,
    'list': ['one', 'two']
}
>>> b = {
    'list': ['two', 'one'],
    'number': 1
}
>>> equal(a, b)
True
2
Yas

Der folgende Code hilft Ihnen, die Liste der Wörter in Python zu vergleichen 

def compate_generic_types(object1, object2):
    if isinstance(object1, str) and isinstance(object2, str):
        return object1 == object2
    Elif isinstance(object1, unicode) and isinstance(object2, unicode):
        return object1 == object2
    Elif isinstance(object1, bool) and isinstance(object2, bool):
        return object1 == object2
    Elif isinstance(object1, int) and isinstance(object2, int):
        return object1 == object2
    Elif isinstance(object1, float) and isinstance(object2, float):
        return object1 == object2
    Elif isinstance(object1, float) and isinstance(object2, int):
        return object1 == float(object2)
    Elif isinstance(object1, int) and isinstance(object2, float):
        return float(object1) == object2

    return True

def deep_list_compare(object1, object2):
    retval = True
    count = len(object1)
    object1 = sorted(object1)
    object2 = sorted(object2)
    for x in range(count):
        if isinstance(object1[x], dict) and isinstance(object2[x], dict):
            retval = deep_dict_compare(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False
        Elif isinstance(object1[x], list) and isinstance(object2[x], list):
            retval = deep_list_compare(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False
        else:
            retval = compate_generic_types(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False

    return retval

def deep_dict_compare(object1, object2):
    retval = True

    if len(object1) != len(object2):
        return False

    for k in object1.iterkeys():
        obj1 = object1[k]
        obj2 = object2[k]
        if isinstance(obj1, list) and isinstance(obj2, list):
            retval = deep_list_compare(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False

        Elif isinstance(obj1, dict) and isinstance(obj2, dict):
            retval = deep_dict_compare(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False
        else:
            retval = compate_generic_types(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False

    return retval
1
Vitthal Kadam

Ich verwende diese Lösung, die in Python 3 perfekt für mich funktioniert


import logging
log = logging.getLogger(__name__)

...

    def deep_compare(self,left, right, level=0):
        if type(left) != type(right):
            log.info("Exit 1 - Different types")
            return False

        Elif type(left) is dict:
            # Dict comparison
            for key in left:
                if key not in right:
                    log.info("Exit 2 - missing {} in right".format(key))
                    return False
                else:
                    if not deep_compare(left[str(key)], right[str(key)], level +1 ):
                        log.info("Exit 3 - different children")
                        return False
            return True
        Elif type(left) is list:
            # List comparison
            for key in left:
                if key not in right:
                    log.info("Exit 4 - missing {} in right".format(key))
                    return False
                else:
                    if not deep_compare(left[left.index(key)], right[right.index(key)], level +1 ):
                        log.info("Exit 5 - different children")
                        return False
            return True
        else:
            # Other comparison
            return left == right

        return False

Es vergleicht dict, list und alle anderen Typen, die den "==" - Operator selbst implementieren . Wenn Sie etwas anderes vergleichen möchten, müssen Sie einen neuen Zweig im "if-Baum" hinzufügen.

Hoffentlich hilft das.

1
Giovanni Basolu

In meiner Antwort zu spät zu sein, ist besser als nie!

Compare Not_Equal ist effizienter als Equal. Daher sind zwei Diktate nicht gleich, wenn in dem anderen Diktat keine Schlüsselwerte gefunden werden. Der folgende Code berücksichtigt, dass Sie möglicherweise den Standarddict vergleichen und daher statt getitem [] get verwenden. 

Verwenden Sie eine Art zufälliger Wert als Standardwert für den get-Aufruf, der dem abgerufenen Schlüssel entspricht - nur für den Fall, dass die Diktate in einem Diktat den Wert Keine haben und dieser Schlüssel nicht im anderen vorhanden ist. Auch die get! = - Bedingung wird vor der Nicht-in-Zustand-Prüfung auf Effizienz geprüft, da Sie die Tasten und Werte von beiden Seiten gleichzeitig überprüfen.

def Dicts_Not_Equal(first,second):
    """ return True if both do not have same length or if any keys and values are not the same """
    if len(first) == len(second): 
        for k in first:
            if first.get(k) != second.get(k,k) or k not in second: return (True)
        for k in second:         
            if first.get(k,k) != second.get(k) or k not in first: return (True)
        return (False)   
    return (True)
0
user-asterix

siehe Wörterbuchansichtobjekte: https://docs.python.org/2/library/stdtypes.html#dict

Auf diese Weise können Sie dictView2 von dictView1 subtrahieren, und es wird eine Reihe von Schlüssel/Wert-Paaren zurückgegeben, die sich in dictView2 unterscheiden:

original = {'one':1,'two':2,'ACTION':'ADD'}
originalView=original.viewitems()
updatedDict = {'one':1,'two':2,'ACTION':'REPLACE'}
updatedDictView=updatedDict.viewitems()
delta=original | updatedDict
print delta
>>set([('ACTION', 'REPLACE')])

Sie können diese Dictionary-Ansichtsobjekte schneiden, vereinigen, unterscheiden (oben gezeigt) und symmetrisch.
Besser? Schneller? - nicht sicher, aber Teil der Standardbibliothek - was die Portabilität zu einem großen Plus macht

0
tranimatronic

In Python 3.6 kann dies folgendermaßen durchgeführt werden:

if (len(dict_1)==len(dict_2): 
  for i in dict_1.items():
        ret=bool(i in dict_2.items())

die Variable ret ist wahr, wenn alle Elemente von dict_1 in dict_2 vorhanden sind

0
Souravi Sinha
>>> hash_1
{'a': 'foo', 'b': 'bar'}
>>> hash_2
{'a': 'foo', 'b': 'bar'}
>>> set_1 = set (hash_1.iteritems())
>>> set_1
set([('a', 'foo'), ('b', 'bar')])
>>> set_2 = set (hash_2.iteritems())
>>> set_2
set([('a', 'foo'), ('b', 'bar')])
>>> len (set_1.difference(set_2))
0
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...    print "The two hashes match."
...
The two hashes match.
>>> hash_2['c'] = 'baz'
>>> hash_2
{'a': 'foo', 'c': 'baz', 'b': 'bar'}
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...     print "The two hashes match."
...
>>>
>>> hash_2.pop('c')
'baz'

Hier ist eine weitere Option:

>>> id(hash_1)
140640738806240
>>> id(hash_2)
140640738994848

Wie Sie sehen, unterscheiden sich die beiden IDs. Aber die rich-Vergleichsoperatoren scheinen den Trick zu vollbringen:

>>> hash_1 == hash_2
True
>>>
>>> hash_2
{'a': 'foo', 'b': 'bar'}
>>> set_2 = set (hash_2.iteritems())
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...     print "The two hashes match."
...
The two hashes match.
>>>
0
Led Zeppelin

In PyUnit gibt es eine Methode, die Wörterbücher schön vergleicht. Ich habe es mit den folgenden zwei Wörterbüchern getestet, und es macht genau das, was Sie suchen.

d1 = {1: "value1",
      2: [{"subKey1":"subValue1",
           "subKey2":"subValue2"}]}
d2 = {1: "value1",
      2: [{"subKey2":"subValue2",
           "subKey1": "subValue1"}]
      }


def assertDictEqual(self, d1, d2, msg=None):
        self.assertIsInstance(d1, dict, 'First argument is not a dictionary')
        self.assertIsInstance(d2, dict, 'Second argument is not a dictionary')

        if d1 != d2:
            standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
            diff = ('\n' + '\n'.join(difflib.ndiff(
                           pprint.pformat(d1).splitlines(),
                           pprint.pformat(d2).splitlines())))
            standardMsg = self._truncateMessage(standardMsg, diff)
            self.fail(self._formatMessage(msg, standardMsg))

Ich empfehle nicht, unittest in Ihren Produktionscode zu importieren. Meiner Meinung nach könnte die Quelle in PyUnit für den Betrieb in der Produktion umgerüstet werden. Es verwendet pprint, das die Wörterbücher "hübsch" druckt. Es scheint ziemlich einfach zu sein, diesen Code "produktionsbereit" anzupassen.

0
MikeyE

Hier ist meine Antwort, verwenden Sie einen rekursiven Weg:

def dict_equals(da, db):
    if not isinstance(da, dict) or not isinstance(db, dict):
        return False
    if len(da) != len(db):
        return False
    for da_key in da:
        if da_key not in db:
            return False
        if not isinstance(db[da_key], type(da[da_key])):
            return False
        if isinstance(da[da_key], dict):
            res = dict_equals(da[da_key], db[da_key])
            if res is False:
                return False
        Elif da[da_key] != db[da_key]:
            return False
    return True

a = {1:{2:3, 'name': 'cc', "dd": {3:4, 21:"nm"}}}
b = {1:{2:3, 'name': 'cc', "dd": {3:4, 21:"nm"}}}
print dict_equals(a, b)

Hoffentlich hilft das!

0
William Xu