Calculer la différence entre les clés contenues dans deux dictionnaires Python

171

Supposons que j'ai deux dictionnaires Python - dictAet dictB. J'ai besoin de savoir s'il y a des clés qui sont présentes dictBmais pas dedans dictA. Quelle est la manière la plus rapide d'y parvenir?

Dois-je convertir les clés du dictionnaire en un ensemble, puis continuer?

Intéressé à connaître vos pensées ...


Merci pour vos réponses.

Toutes mes excuses pour ne pas avoir posé correctement ma question. Mon scénario est le suivant - j'ai un dictAqui peut être le même que dictBou peut avoir des clés manquantes par rapport à dictBou bien la valeur de certaines clés peut être différente et doit être définie sur celle de dictAla valeur de la clé.

Le problème est que le dictionnaire n'a pas de norme et peut avoir des valeurs qui peuvent être dictées par dict.

Dire

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

Ainsi, la valeur 'key2' doit être réinitialisée à la nouvelle valeur et 'key13' doit être ajoutée à l'intérieur du dict. La valeur de clé n'a pas de format fixe. Il peut s'agir d'une simple valeur ou d'un dict ou d'un dict de dict.

Nathan Davis
la source

Réponses:

234

Vous pouvez utiliser les opérations définies sur les touches:

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

Voici une classe pour trouver toutes les possibilités: ce qui a été ajouté, ce qui a été supprimé, quelles paires clé-valeur sont les mêmes et quelles paires clé-valeur sont modifiées.

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

Voici un exemple de sortie:

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

Disponible en tant que dépôt github: https://github.com/hughdbrown/dictdiffer

Hughdbrown
la source
3
Solution intelligente, merci! Je l'ai fait fonctionner avec des dictionnaires imbriqués en vérifiant si les valeurs modifiées ou inchangées sont des instances de dict et en appelant une fonction récursive pour les vérifier à nouveau en utilisant votre classe.
AJJ
1
@AJJ J'adorerais voir cette implémentation.
urschrei
1
Que diriez-vous d'un def update(self, new_dict): self.__init__(new_dict, self.current_dict)ou autre pour pouvoir faire une comparaison glissante
Nick T
Quelques remarques: la DictDifferclasse est une classe sans état et pourrait être une fonction. Les valeurs changedet unchangedpeuvent être calculées dans la même boucle. Ces deux fonctions pourraient renvoyer a listau lieu de a setce qui est certainement moins cher. Pour une comparaison approfondie, vous pouvez jeter un œil au framework de test unitaire: docs.python.org/2/library/unittest.html , suivez simplement la assertDictEqualméthode dans le code source.
Laurent LAPORTE
1
FWIW, set(dictb)est probablement mieux que set(dictb.keys()).
mgilson
60

Au cas où vous voudriez la différence récursivement, j'ai écrit un paquet pour python: https://github.com/seperman/deepdiff

Installation

Installer depuis PyPi:

pip install deepdiff

Exemple d'utilisation

Importation

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

Le même objet retourne vide

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

Le type d'article a changé

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

La valeur d'un article a changé

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

Article ajouté et / ou supprimé

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

Différence de cordes

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

Différence de cordes 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

Changement de type

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

Différence de liste

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

Différence de liste 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}}}

Différence de liste en ignorant l'ordre ou les doublons: (avec les mêmes dictionnaires que ci-dessus)

>>> 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 contenant le dictionnaire:

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

Ensembles:

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

Tuples nommés:

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

Objets personnalisés:

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

Attribut d'objet ajouté:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
Seperman
la source
Merci pour ça! Juste mis en œuvre sur mon projet, fonctionne très bien!
gtalarico
1
@gtalarico Heureux de vous aider! Merci pour ces mots gentils!
Seperman
existe-t-il une option pour ignorer la différence d' ordre de la liste ? parce que mon application ne s'en soucie pas.
Lei Yang
Beau projet, a fait tout le travail avec un minimum d'effort de ma part. Merci!
Stanislav Tsepa le
@LeiYang Oui, vous pouvez définir ignore_order=True. Vous pouvez trouver la documentation sur deepdiff.readthedocs.io/en/latest/diff.html
Seperman
18

je ne sais pas si c'est "rapide" ou pas, mais normalement, on peut le faire

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
la source
Vous devez échanger dictaet dictbpuisqu'il veut savoir que ces clés dictbne sont pas dedans dicta.
Gumbo
2
for key in dicta.keys():=>for key in dicta:
Jean-François Fabre
15

Comme l'a écrit Alex Martelli, si vous voulez simplement vérifier si une clé de B n'est pas dans A, ce any(True for k in dictB if k not in dictA)serait la voie à suivre.

Pour trouver les clés manquantes:

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

Donc, ces deux solutions sont à peu près la même vitesse.

Jochen Ritzel
la source
8
Cela a plus de sens:any(k not in dictA for k in dictB)
hughdbrown
13

Si vous voulez vraiment dire exactement ce que vous dites (que vous avez seulement besoin de savoir SI "il y a des clés" en B et non en A, pas QUELLES peuvent-elles être le cas échéant), le moyen le plus rapide devrait être:

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

Si vous avez réellement besoin de savoir QUELLES CLÉS, le cas échéant, sont en B et non en A, et pas seulement "SI" il y a de telles clés, alors les réponses existantes sont tout à fait appropriées (mais je suggère plus de précision dans les questions futures si c'est en effet ce que tu veux dire ;-).

Alex Martelli
la source
8

Utilisezset() :

set(dictA.keys()).intersection(dictB.keys())
Joziel Costa
la source
set(d)renvoie déjà uniquement les clés, donc vous pouvez le faireset(da).intersection(db)
chiffre
8

La meilleure réponse de Hughdbrown suggère d'utiliser la différence d'ensemble, ce qui est certainement la meilleure approche:

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

Le problème avec ce code est qu'il construit deux listes juste pour créer deux ensembles, il perd donc 4N de temps et 2N d'espace. C'est aussi un peu plus compliqué que nécessaire.

Habituellement, ce n'est pas un gros problème, mais si c'est le cas:

diff = dictb.keys() - dicta

Python 2

Dans Python 2, keys()renvoie une liste des clés, pas un fichier KeysView. Il faut donc demander viewkeys()directement.

diff = dictb.viewkeys() - dicta

Pour le code à double version 2.7 / 3.x, nous espérons que vous utilisez sixou quelque chose de similaire, vous pouvez donc utiliser six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

En 2.4-2.6, il n'y a pas de KeysView. Mais vous pouvez au moins réduire le coût de 4N à N en construisant votre ensemble de gauche directement à partir d'un itérateur, au lieu de créer d'abord une liste:

diff = set(dictb) - dicta

Articles

J'ai un dictA qui peut être le même que dictB ou peut avoir des clés manquantes par rapport à dictB ou bien la valeur de certaines clés peut être différente

Vous n'avez donc pas vraiment besoin de comparer les clés, mais les éléments. An ItemsViewn'est a que Setsi les valeurs sont hachables, comme les chaînes. S'ils le sont, c'est facile:

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

Diff récursif

Bien que la question ne demande pas directement un diff récursif, certaines des valeurs d'exemple sont des dicts, et il semble que la sortie attendue les diffère récursivement. Il y a déjà plusieurs réponses ici montrant comment faire cela.

Abarnert
la source
la réponse définitive de 2018.
Jean-François Fabre
@ Jean-FrançoisFabre Bien sûr, le truc Python 2.4-2.6 est déjà assez hors de propos en 2018…
abarnert
certaines personnes sont coincées avec 2.6
Jean-François Fabre
3

Voici une méthode qui fonctionnera, permet aux clés qui évaluent à False, et utilise toujours une expression de générateur pour tomber tôt si possible. Ce n'est pas exceptionnellement joli cependant.

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

ÉDITER:

THC4k a publié une réponse à mon commentaire sur une autre réponse. Voici une meilleure et plus jolie façon de faire ce qui précède:

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

Je ne sais pas comment cela ne m'a jamais traversé l'esprit ...

Steve Losh
la source
c'est la même réponse que la réponse précédente d'Alex Martelli
Jean-François Fabre
C'est maintenant. Quand je l'ai posté (il y a neuf ans, lol), la réponse précédente était any(k for k in dictB if k not in dictA)ce qui n'est pas la même chose (pour les fausses clés). Vérifiez l'historique des modifications / les horodatages.
Steve Losh
3

C'est une vieille question et demande un peu moins que ce dont j'avais besoin, donc cette réponse résout en fait plus que cette question ne le demande. Les réponses à cette question m'ont aidé à résoudre les problèmes suivants:

  1. (demandé) Notez les différences entre deux dictionnaires
  2. Fusionner les différences du n ° 1 dans le dictionnaire de base
  3. (demandé) Fusionner les différences entre deux dictionnaires (traiter le dictionnaire # 2 comme s'il s'agissait d'un dictionnaire diff)
  4. Essayez de détecter les mouvements d'articles ainsi que les changements
  5. (demandé) Faites tout cela récursivement

Tout cela combiné avec JSON permet un support de stockage de configuration assez puissant.

La solution ( également sur 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
la source
2

qu'en est-il de standart (comparer FULL Object)

PyDev-> nouveau module PyDev-> Module: 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
la source
C'est merveilleux si vous avez un énorme dictionnaire imbriqué et que vous voulez comparer tout ce qu'il contient et voir les différences. Merci!
Matthew Moisen
2

Si sur 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
la source
1

Voici une solution pour comparer en profondeur 2 clés de dictionnaires:

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
la source
1

voici une solution qui permet de comparer plus de deux dictats:

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

exemple d'utilisation:

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

Ma recette de différence symétrique entre deux dictionnaires:

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)

Et le résultat est:

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

Comme mentionné dans d'autres réponses, unittest produit une sortie intéressante pour comparer les dictionnaires, mais dans cet exemple, nous ne voulons pas avoir à construire un test complet en premier.

En grattant la source la plus simple, il semble que vous pouvez obtenir une solution équitable avec juste ceci:

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

alors

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)

Résulte en:

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

+  4: 111}
?        ^

-  5: 110}

Où:

  • '-' indique les clés / valeurs dans le premier mais pas dans le second dict
  • '+' indique les clés / valeurs dans le second mais pas dans le premier dict

Comme dans unittest, la seule mise en garde est que le mappage final peut être considéré comme un diff, en raison de la virgule / crochet de fin.

Ryan de Kleer
la source
1

@Maxx a une excellente réponse, utilisez les unittestoutils fournis par 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)

Ensuite, n'importe où dans votre code, vous pouvez appeler:

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

La sortie résultante ressemble à la sortie de diff, en imprimant joliment les dictionnaires avec +ou en -ajoutant chaque ligne qui est différente.

Brent Washburne
la source
0

Je ne sais pas si c'est toujours pertinent mais je suis tombé sur ce problème, ma situation j'avais juste besoin de retourner un dictionnaire des changements pour tous les dictionnaires imbriqués, etc. Je n'ai pas pu trouver une bonne solution mais j'ai fini par écrire une fonction simple pour faire ça . J'espère que cela t'aides,

Jonathan Mickle
la source
2
Il serait préférable d'avoir la plus petite quantité de code qui résout le problème de l'OP dans la réponse, au lieu d'un lien. Si le lien meurt ou bouge, votre réponse devient inutile.
George Stocker
0

Si vous voulez une solution intégrée pour une comparaison complète avec des structures dict arbitraires, la réponse de @ Maxx est un bon début.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)
Brad
la source
Vous ne pouvez apparemment pas instancier une classe de test comme celle-là, ce qui est dommage.
Ben Liyanage
0

Basé sur la réponse de ghostdog74,

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

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

affichera une valeur différente de dicta

normalUser
la source
0

Essayez ceci pour trouver l'intersection, les clés qui sont dans les deux dictionarie, si vous voulez que les clés ne se trouvent pas sur la deuxième dictionarie, utilisez simplement le not in ...

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