55 votes

Comparaison rapide entre deux dictionnaires Python

Supposons que j'ai deux dictionnaires Python - dictA y dictB . J'ai besoin de savoir s'il y a des clés qui sont présentes dans dictB mais pas dans dictA . Quel est le moyen le plus rapide d'y parvenir ?

Dois-je convertir les clés du dictionnaire en un ensemble et procéder ensuite ?

J'aimerais savoir ce que vous en pensez...


Merci pour vos réponses.

Je m'excuse de ne pas avoir formulé ma question correctement. Mon scénario est le suivant : j'ai un dictA qui peut être la même chose que dictB ou peut avoir quelques clés manquantes par rapport à dictB ou bien la valeur de certaines clés pourrait être différente, ce qui doit être mis à celle de dictA la valeur de la clé.

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

Dites

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 de 'key2' doit être réinitialisée à la nouvelle valeur et 'key13' doit être ajoutée dans le dict. La valeur de la clé n'a pas un format fixe. Il peut s'agir d'une simple valeur ou d'un dict ou d'un dict de dict.

2voto

tzot Points 32224

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

1voto

Steve Losh Points 11958

Voici une méthode qui fonctionnera et qui permet d'utiliser des clés qui évaluent à False et utilise toujours une expression de générateur pour tomber tôt si possible. Mais ce n'est pas exceptionnellement joli.

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

EDITAR:

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

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

Je ne sais pas pourquoi ça ne m'a jamais traversé l'esprit...

1voto

Maxx Points 27

Qu'en est-il du standart (comparaison FULL Object) ?

PyDev->new PyDev Module->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()

0voto

velis Points 641

Il s'agit d'une ancienne question et elle demande un peu moins que ce dont j'avais besoin, donc cette réponse résout en fait plus que cette question ne demande. Les réponses à cette question m'ont aidé à résoudre les problèmes suivants :

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

Tout cela, combiné à JSON, constitue 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

-1voto

roborob Points 11

Cela ne répond peut-être pas entièrement à votre question, mais vous pourriez vider les dictionnaires en json et comparer les chaînes de caractères résultantes ;-)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X