Calcular a diferença nas chaves contidas em dois dicionários Python

171

Suponha que eu tenha dois dicionários Python - dictAe dictB. Preciso descobrir se existem chaves presentes ou dictBnão dictA. Qual é a maneira mais rápida de fazer isso?

Devo converter as chaves do dicionário em um conjunto e depois continuar?

Interessado em conhecer seus pensamentos ...


Obrigado por suas respostas.

Desculpas por não ter feito a pergunta corretamente. Meu cenário é o seguinte: eu tenho um dictAque pode ser o mesmo dictBou pode ter algumas chaves ausentes em comparação com dictBou o valor de algumas chaves pode ser diferente, o que deve ser definido como dictAo valor da chave.

O problema é que o dicionário não tem padrão e pode ter valores que podem ser dict of dict.

Dizer

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

Portanto, o valor 'key2' deve ser redefinido para o novo valor e 'key13' deve ser adicionado dentro do dict. O valor da chave não possui um formato fixo. Pode ser um valor simples ou um ditado ou ditado.

Nathan Davis
fonte

Respostas:

234

Você pode usar operações definidas nas teclas:

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

Aqui está uma classe para encontrar todas as possibilidades: o que foi adicionado, o que foi removido, quais pares de valores-chave são iguais e quais pares de valores-chave são alterados.

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

Aqui está um exemplo de saída:

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

Disponível como um repositório do github: https://github.com/hughdbrown/dictdiffer

hughdbrown
fonte
3
Solução inteligente, obrigado! Eu o fiz funcionar com dict aninhados, verificando se os valores alterados ou inalterados são instâncias de dict e chamando uma função recursiva para verificá-los novamente usando sua classe.
1112 AJJ
1
@AJJ Eu adoraria ver essa implementação.
2141313 urschrei
1
Como cerca de um def update(self, new_dict): self.__init__(new_dict, self.current_dict)ou similar para que você possa fazer uma comparação rolando
Nick T
Algumas observações: a DictDifferclasse é uma classe sem estado e pode ser uma função. Os valores changede unchangedpodem ser calculados no mesmo loop. Essas duas funções podem retornar um em listvez de um setque certamente é menos caro. Para uma comparação profunda, você pode dar uma olhada na estrutura de teste da unidade: docs.python.org/2/library/unittest.html , basta seguir o assertDictEqualmétodo no código-fonte.
Laurent LAPORTE
1
FWIW, set(dictb)provavelmente é melhor que set(dictb.keys()).
mgilson
60

Caso você queira a diferença recursivamente, escrevi um pacote para python: https://github.com/seperman/deepdiff

Instalação

Instale a partir do PyPi:

pip install deepdiff

Exemplo de uso

Importação

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

O mesmo objeto retorna vazio

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

O tipo de um item foi alterado

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

O valor de um item foi alterado

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

Item adicionado e / ou removido

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

Diferença de cadeia

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

Diferença de string 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

Alteração 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]}}}

Diferença de lista

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

Diferença de lista 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}}}

Listar a diferença de ordem de ignição ou duplicatas: (com os mesmos dicionários acima)

>>> 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 contém o dicionário:

>>> 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 nomeadas:

>>> 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 adicionado:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
Seperman
fonte
Obrigado por isso! Apenas implementado no meu projeto, funciona muito bem!
gtalarico
1
@gtalarico Feliz em ajudar! Obrigado pelas palavras amáveis!
Seperman
existe alguma opção para ignorar a diferença de ordem da lista ? porque meu aplicativo não se importa com isso.
Lei Yang
Bom projeto, fiz todo o trabalho com o mínimo de esforço do meu lado. Obrigado!
Stanislav Tsepa
@LeiYang Sim, você pode definir ignore_order=True. Você pode encontrar os documentos em deepdiff.readthedocs.io/en/latest/diff.html
Seperman
18

não tenho certeza se é "rápido" ou não, mas normalmente, pode-se fazer isso

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
fonte
Você tem que trocar dictae dictbjá que ele quer saber essas chaves dictbnão estão dicta.
Gumbo
2
for key in dicta.keys():=>for key in dicta:
Jean-François Fabre
15

Como escreveu Alex Martelli, se você simplesmente deseja verificar se alguma chave em B não está em A, any(True for k in dictB if k not in dictA)seria o caminho a seguir.

Para encontrar as chaves que estão faltando:

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

Portanto, essas duas soluções têm praticamente a mesma velocidade.

Jochen Ritzel
fonte
8
Isso faz mais sentido:any(k not in dictA for k in dictB)
hughdbrown
13

Se você realmente quer dizer exatamente o que diz (que você só precisa descobrir se "existem chaves" em B e não em A, e NÃO QUAIS SÃO, se houver), o caminho mais rápido deve ser:

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

Se você realmente precisa descobrir QUAIS CHAVES, se houver alguma, estão em B e não em A, e não apenas "SE" existem essas chaves, as respostas existentes são bastante apropriadas (mas sugiro mais precisão em perguntas futuras, se for o caso). na verdade o que você quer dizer ;-).

Alex Martelli
fonte
8

Useset() :

set(dictA.keys()).intersection(dictB.keys())
Joziel Costa
fonte
set(d)já retorna apenas as teclas, para que você possa fazerset(da).intersection(db)
numeral
8

A resposta principal de hughdbrown sugere o uso de diferenças definidas, que é definitivamente a melhor abordagem:

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

O problema com esse código é que ele cria duas listas apenas para criar dois conjuntos, portanto, está perdendo tempo de 4N e espaço de 2N. Também é um pouco mais complicado do que precisa ser.

Normalmente, isso não é grande coisa, mas se for:

diff = dictb.keys() - dicta

Python 2

No Python 2, keys()retorna uma lista das chaves, não a KeysView. Então você tem que pedir viewkeys()diretamente.

diff = dictb.viewkeys() - dicta

Para o código da versão dupla 2.7 / 3.x, espero que você esteja usando sixou algo semelhante, para poder usar six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

Em 2.4-2.6, não há KeysView. Mas você pode pelo menos reduzir o custo de 4N para N construindo seu conjunto esquerdo diretamente de um iterador, em vez de criar uma lista primeiro:

diff = set(dictb) - dicta

Itens

Eu tenho um dictA que pode ser o mesmo que dictB ou pode ter algumas chaves ausentes em comparação com dictB ou o valor de algumas chaves pode ser diferente

Então você realmente não precisa comparar as chaves, mas os itens. An ItemsViewé apenas a Setse os valores forem hasháveis, como strings. Se forem, é fácil:

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

Diferença recursiva

Embora a pergunta não esteja solicitando diretamente uma diferença recursiva, alguns dos valores de exemplo são dictos e parece que a saída esperada os diferencia recursivamente. Já existem várias respostas aqui mostrando como fazer isso.

abarnert
fonte
a resposta definitiva de 2018.
Jean-François Fabre
@ Jean-FrançoisFabre É claro que o material do Python 2.4-2.6 já é bastante irrelevante em 2018 ...
abarnert
algumas pessoas estão presas com 2.6
Jean-François Fabre
3

Aqui está uma maneira de funcionar, permitir que as chaves avaliem Falsee ainda use uma expressão geradora para sair mais cedo, se possível. Não é excepcionalmente bonito.

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

EDITAR:

THC4k postou uma resposta ao meu comentário em outra resposta. Aqui está uma maneira melhor e mais bonita de fazer o acima:

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

Não sei como isso nunca passou pela minha cabeça ...

Steve Losh
fonte
esta é a mesma resposta que a resposta anterior de Alex Martelli
Jean-François Fabre
É agora. Quando eu publiquei (nove anos atrás, lol), a resposta anterior foi any(k for k in dictB if k not in dictA)que não é a mesma coisa (para chaves falsey). Verifique o histórico de edição / registros de data e hora.
Steve Losh
3

Esta é uma pergunta antiga e faz um pouco menos do que eu precisava, então essa resposta realmente resolve mais do que essa pergunta. As respostas nesta pergunta me ajudaram a resolver o seguinte:

  1. (solicitado) Registre diferenças entre dois dicionários
  2. Mesclar diferenças de # 1 no dicionário base
  3. (solicitado) Mesclar diferenças entre dois dicionários (tratar o dicionário # 2 como se fosse um dicionário diff)
  4. Tente detectar movimentos de itens e alterações
  5. (solicitado) Faça tudo isso recursivamente

Tudo isso combinado com JSON contribui para um suporte de armazenamento de configuração bastante poderoso.

A solução ( também no 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
fonte
2

e o standart (compare o objeto completo)

PyDev-> novo 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
fonte
Isso é maravilhoso se você tiver um dicionário aninhado enorme e quiser comparar tudo o que estiver dentro e ver as diferenças. Obrigado!
Matthew Moisen
2

Se estiver em 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
fonte
1

Aqui está uma solução para comparar profundamente duas chaves de dicionários:

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

aqui está uma solução que pode comparar mais de dois ditados:

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

exemplo de uso:

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

Minha receita de diferença simétrica entre dois dicionários:

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)

E o resultado é:

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

Como mencionado em outras respostas, o unittest produz uma boa saída para comparar ditados, mas neste exemplo não queremos ter que construir um teste inteiro primeiro.

Rascunhando a fonte mais unida, parece que você pode obter uma solução justa com apenas isso:

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

tão

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)

Resulta em:

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

+  4: 111}
?        ^

-  5: 110}

Onde:

  • '-' indica chave / valores no primeiro, mas não no segundo ditado
  • '+' indica chave / valores no segundo ditado, mas não no primeiro

Como no mais unittest, a única ressalva é que o mapeamento final pode ser considerado um diff, devido à vírgula / colchete à direita.

Ryan de Kleer
fonte
1

O @Maxx tem uma excelente resposta, use as unittestferramentas fornecidas pelo 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)

Em qualquer lugar do seu código, você pode ligar para:

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

A saída resultante se parece com a saída diff, imprimindo os dicionários com +ou -acrescentando cada linha diferente.

Brent Washburne
fonte
0

Não tenho certeza se ainda é relevante, mas me deparei com esse problema, minha situação só precisava retornar um dicionário das alterações para todos os dicionários aninhados, etc. Não foi possível encontrar uma boa solução por aí, mas acabei escrevendo uma função simples para fazer isso . Espero que isto ajude,

Jonathan Mickle
fonte
2
Seria preferível ter a menor quantidade de código que corrige o problema do OP realmente na resposta, em vez de um link. Se o link acabar ou se mover, sua resposta se tornará inútil.
George Stocker
0

Se você deseja uma solução integrada para uma comparação completa com estruturas de dict arbitrárias, a resposta do @ Maxx é um bom começo.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)
Brad
fonte
Aparentemente, você não pode instanciar uma classe de teste como essa, o que é muito ruim.
Ben Liyanage
0

Com base na resposta 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á um valor diferente de dicta

normalUser
fonte
0

Tente isso para encontrar a interseção, as chaves que estão nos dois dictionarie, se você quiser que as chaves não sejam encontradas no segundo dictionarie, basta usar o not in ...

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