Calcular la diferencia en las claves contenidas en dos diccionarios de Python

171

Supongamos que tengo dos diccionarios de Python, dictAy dictB. Necesito averiguar si hay claves que están presentes dictBpero no en dictA. ¿Cuál es la forma más rápida de hacerlo?

¿Debo convertir las claves del diccionario en un conjunto y luego continuar?

Interesado en conocer tus pensamientos ...


Gracias por sus respuestas

Disculpas por no decir mi pregunta correctamente. Mi situación es la siguiente: tengo una dictAque puede ser igual dictBo puede que falten algunas claves en comparación dictBo, de lo contrario, el valor de algunas claves puede ser diferente, lo que debe establecerse en dictAel valor de la clave.

El problema es que el diccionario no tiene un estándar y puede tener valores que pueden ser dict de dict.

Decir

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

Por lo tanto, el valor 'clave2' debe restablecerse al nuevo valor y 'clave13' debe agregarse dentro del dict. El valor clave no tiene un formato fijo. Puede ser un valor simple o un dict o un dict de dict.

Nathan Davis
fuente

Respuestas:

234

Puede usar las operaciones establecidas en las teclas:

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

Aquí hay una clase para encontrar todas las posibilidades: qué se agregó, qué se eliminó, qué pares clave-valor son los mismos y qué pares clave-valor se cambian.

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

Aquí hay algunos resultados de muestra:

>>> 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 como repositorio de github: https://github.com/hughdbrown/dictdiffer

hughdbrown
fuente
3
Solución inteligente, gracias! Lo hice funcionar con dictados anidados comprobando si los valores modificados o no son instancias dict y llamando a una función recursiva para verificarlos nuevamente usando su clase.
AJJ
1
@AJJ Me encantaría ver esa implementación.
urschrei
1
¿Qué tal una def update(self, new_dict): self.__init__(new_dict, self.current_dict)o similar para que pueda hacer una comparación continua
Nick T
Algunas observaciones: la DictDifferclase es una clase sin estado y podría ser una función. Los valores changedy unchangedpodrían calcularse en el mismo bucle. Estas dos funciones podrían devolver una en listlugar de una setque ciertamente es menos costosa. Para una comparación profunda, puede echar un vistazo al marco de prueba de la Unidad: docs.python.org/2/library/unittest.html , solo siga el assertDictEqualmétodo en el código fuente.
Laurent LAPORTE
1
FWIW, set(dictb)es probablemente mejor que set(dictb.keys()).
mgilson
60

En caso de que desee la diferencia de forma recursiva, he escrito un paquete para python: https://github.com/seperman/deepdiff

Instalación

Instalar desde PyPi:

pip install deepdiff

Ejemplo de uso

Importador

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

El mismo objeto vuelve vacío

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

El tipo de un artículo ha cambiado

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

El valor de un artículo ha cambiado

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

Artículo agregado y / o eliminado

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

Diferencia de cadena

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

Diferencia de cuerda 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

Cambio de tipo

>>> 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 de diferencia

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

Lista de diferencia 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}}}

Enumere la diferencia ignorando el orden o los duplicados: (con los mismos diccionarios que arriba)

>>> 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 que contiene el diccionario:

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

Conjuntos

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

Tuplas nombradas:

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

Objetos personalizados:

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

Atributo de objeto agregado:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
Seperman
fuente
¡Gracias por esto! ¡Acabo de implementar en mi proyecto, funciona muy bien!
gtalarico
1
@gtalarico ¡Feliz de ayudar! ¡Gracias por las palabras amables!
Seperman
¿Hay alguna opción para ignorar la diferencia de orden de la lista ? porque mi aplicación no se preocupa por eso.
Lei Yang
Buen proyecto, hice todo el trabajo con el mínimo esfuerzo de mi parte. ¡Gracias!
Stanislav Tsepa
@LeiYang Sí, puedes configurarlo ignore_order=True. Puede encontrar los documentos en deepdiff.readthedocs.io/en/latest/diff.html
Seperman
18

no estoy seguro de si es "rápido" o no, pero normalmente, uno puede hacer esto

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
fuente
Tienes que intercambiar dictay dictbya que él quiere saber esas claves dictbno están ahí dicta.
Gumbo
2
for key in dicta.keys():=>for key in dicta:
Jean-François Fabre
15

Como escribió Alex Martelli, si simplemente desea verificar si alguna tecla en B no está en A, ese any(True for k in dictB if k not in dictA)sería el camino a seguir.

Para encontrar las claves que faltan:

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

Entonces esas dos soluciones son más o menos la misma velocidad.

Jochen Ritzel
fuente
8
Esto tiene más sentido:any(k not in dictA for k in dictB)
hughdbrown
13

Si realmente quiere decir exactamente lo que dice (que solo necesita averiguar SI "hay claves" en B y no en A, y NO CUÁLES podrían ser esas), la forma más rápida debería ser:

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

Si realmente necesita averiguar QUÉ LLAVES, si las hay, están en B y no en A, y no solo "SI" hay tales llaves, entonces las respuestas existentes son bastante apropiadas (pero sugiero más precisión en futuras preguntas si eso es así) de hecho lo que quieres decir ;-).

Alex Martelli
fuente
8

Usoset() :

set(dictA.keys()).intersection(dictB.keys())
Joziel Costa
fuente
set(d)ya devuelve solo las teclas, así que puede hacerloset(da).intersection(db)
número
8

La respuesta principal de hughdbrown sugiere usar la diferencia establecida, que definitivamente es el mejor enfoque:

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

El problema con este código es que crea dos listas solo para crear dos conjuntos, por lo que está desperdiciando 4N de tiempo y 2N de espacio. También es un poco más complicado de lo que debe ser.

Por lo general, esto no es un gran problema, pero si lo es:

diff = dictb.keys() - dicta

Python 2

En Python 2, keys()devuelve una lista de las claves, no a KeysView. Entonces tienes que pedirlo viewkeys()directamente.

diff = dictb.viewkeys() - dicta

Para el código 2.7 / 3.x de la versión dual, es de esperar que esté usando sixo algo similar, por lo que puede usar six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

En 2.4-2.6, no hay KeysView. Pero al menos puede reducir el costo de 4N a N construyendo su conjunto izquierdo directamente desde un iterador, en lugar de crear primero una lista:

diff = set(dictb) - dicta

Artículos

Tengo un dictA que puede ser el mismo que dictB o puede que falten algunas claves en comparación con dictB o, de lo contrario, el valor de algunas claves puede ser diferente

Así que realmente no necesita comparar las claves, sino los elementos. An ItemsViewes solo a Setsi los valores son hashables, como las cadenas. Si lo son, es fácil:

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

Dif recursivo

Aunque la pregunta no es pedir directamente una diferencia recursiva, algunos de los valores de ejemplo son dictados, y parece que la salida esperada los difiere recursivamente. Ya hay varias respuestas aquí que muestran cómo hacerlo.

abarnert
fuente
la respuesta definitiva de 2018.
Jean-François Fabre
@ Jean-FrançoisFabre Por supuesto, las cosas de Python 2.4-2.6 ya son bastante irrelevantes en 2018 ...
abarnert
algunas personas están atrapadas con 2.6
Jean-François Fabre
3

Aquí hay una manera que funcionará, permite claves que evalúan Falsey aún utiliza una expresión generadora para caerse temprano si es posible. Sin embargo, no es excepcionalmente bonito.

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

EDITAR:

THC4k publicó una respuesta a mi comentario en otra respuesta. Aquí hay una forma mejor y más bonita de hacer lo anterior:

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

No estoy seguro de cómo eso nunca se me pasó por la cabeza ...

Steve Losh
fuente
esta es la misma respuesta que la respuesta anterior de Alex Martelli
Jean-François Fabre
Esto es ahora. Cuando lo publiqué (hace nueve años, jajaja) la respuesta anterior fue any(k for k in dictB if k not in dictA)que no es lo mismo (para las llaves falsey). Verifique el historial de edición / marcas de tiempo.
Steve Losh
3

Esta es una pregunta antigua y hace un poco menos de lo que necesitaba, por lo que esta respuesta realmente resuelve más de lo que pregunta. Las respuestas en esta pregunta me ayudaron a resolver lo siguiente:

  1. (preguntado) Registre las diferencias entre dos diccionarios
  2. Combinar diferencias del # 1 en el diccionario base
  3. (preguntado) Combine las diferencias entre dos diccionarios (trate el diccionario # 2 como si fuera un diccionario de diferencias)
  4. Intenta detectar movimientos de elementos y cambios
  5. (preguntado) Haga todo esto recursivamente

Todo esto combinado con JSON lo convierte en un soporte de almacenamiento de configuración bastante potente.

La solución ( también en 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
fuente
2

¿Qué pasa con standart (comparar objeto completo)

PyDev-> nuevo Módulo PyDev-> Módulo: 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
fuente
Esto es maravilloso si tienes un gran diccionario anidado y quieres comparar todo lo que hay dentro y ver las diferencias. ¡Gracias!
Matthew Moisen
2

Si en 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
fuente
1

Aquí hay una solución para comparar en profundidad 2 claves de diccionarios:

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
fuente
1

Aquí hay una solución que puede comparar más de dos dictados:

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

ejemplo de uso:

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

Mi receta de diferencia simétrica entre dos diccionarios:

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)

Y el resultado es:

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

Como se mencionó en otras respuestas, unittest produce una buena salida para comparar dictos, pero en este ejemplo no queremos tener que construir primero una prueba completa.

Al eliminar la fuente de prueba de la unidad, parece que puede obtener una solución justa con solo esto:

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

entonces

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)

Resultados en:

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

+  4: 111}
?        ^

-  5: 110}

Dónde:

  • '-' indica clave / valores en el primer pero no en el segundo dict
  • '+' indica clave / valores en el segundo pero no en el primer dict

Al igual que en unittest, la única advertencia es que el mapeo final puede considerarse como una diferencia, debido a la coma / corchete final.

Ryan de Kleer
fuente
1

@Maxx tiene una excelente respuesta, use las unittestherramientas proporcionadas por 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)

Luego, en cualquier parte de su código puede llamar:

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

El resultado resultante se parece al resultado diff, imprimiendo bonitos los diccionarios con +o -anteponiendo cada línea que es diferente.

Brent Washburne
fuente
0

No estoy seguro de si todavía es relevante, pero me encontré con este problema, mi situación solo necesitaba devolver un diccionario de los cambios para todos los diccionarios anidados, etc. No pude encontrar una buena solución, pero terminé escribiendo una función simple para hacer esto . Espero que esto ayude,

Jonathan Mickle
fuente
2
Sería preferible tener la menor cantidad de código que solucione el problema del OP en la respuesta, en lugar de un enlace. Si el enlace muere o se mueve, su respuesta se vuelve inútil.
George Stocker
0

Si desea una solución integrada para una comparación completa con estructuras de dict arbitrarias, la respuesta de @ Maxx es un buen comienzo.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)
Puntilla
fuente
Aparentemente no puede crear una instancia de una clase de prueba como esa, lo cual es una lástima.
Ben Liyanage
0

Basado en la respuesta 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

imprimirá un valor diferente de dicta

usuario normal
fuente
0

Intente esto para encontrar la intersección, las teclas que están en ambas direcciones, si desea que las llaves no se encuentren en la segunda memoria, simplemente use el no en ...

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