Comparar dois dicionários em Python

Tenho dois dicionários, mas, para simplificar, tomo estes dois:
>>> x = dict(a=1, b=2)
>>> y = dict(a=2, b=2)

Agora, quero comparar se cada par key, value em x tem o mesmo valor correspondente em y. Então escrevi isto:

>>> 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
E funciona desde que um tuple é devolvido e depois comparado para a igualdade.

As minhas perguntas:

Isto está correcto? Há uma maneira melhor de fazer isto? É melhor não ser rápido, estou a falar de elegância de código.

actualização: esqueci-me de mencione que eu tenho que verificar quantos pares key, value são iguais.

Author: user225312, 2010-12-24

17 answers

Se você quer saber quantos valores coincidem em ambos os dicionários, você deveria ter dito que:)

Talvez algo assim:
shared_items = {k: x[k] for k in x if k in y and x[k] == y[k]}
print len(shared_items)
 123
Author: mouad, 2018-06-14 13:15:51
O que queres fazer é simplesmente ... x==y

O que você faz não é uma boa idéia, porque os itens em um dicionário não devem ter qualquer ordem. Você pode estar comparando [('a',1),('b',1)] com [('b',1), ('a',1)] (mesmos dicionários, ordem diferente).

Por exemplo, veja isto:

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

A diferença é apenas um item, mas o seu algoritmo verá que todos os itens são diferentes

 138
Author: Jochen Ritzel, 2010-12-24 19:25:16
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)
 118
Author: Daniel Myers, 2013-09-17 22:02:10
Sou novo no python, mas acabei por fazer algo semelhante ao @mouad
unmatched_item = set(dict_1.items()) ^ set(dict_2.items())
len(unmatched_item) # should be 0

O operador XOR (^) deve eliminar todos os elementos do dict quando são os mesmos em ambos os dict.

 48
Author: philipp, 2013-06-13 19:04:40

Usa apenas:

assert cmp(dict1, dict2) == 0
 40
Author: Shiyu, 2015-04-29 00:51:26

Para verificar se dois dicionários têm o mesmo conteúdo, basta usar:

dic1 == dic2

De documentos python:

Para ilustrar, os seguintes exemplos devolvem um dicionário igual a {"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
 34
Author: Pedro Lobito, 2018-09-06 11:35:06

A resposta de@mouad é boa se assumires que ambos os dicionários contêm apenas valores simples. No entanto, se você tem dicionários que contêm dicionários, você vai ter uma exceção como dicionários não são hashable.

Por cima da minha cabeça, algo assim pode funcionar.
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
Author: Alexander, 2018-08-16 13:55:11

Outra possibilidade, até a última nota da operação, é comparar os traços (SHA ou MD) dos dictos despejados como JSON. A forma como os hashs são construídos garante que se eles são iguais, as strings fonte são iguais também. Isto é muito rápido e matematicamente som.

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
Author: WoJ, 2015-03-15 08:11:00
Já que parece que ninguém mencionou deepdiff, acrescentá-lo-ei aqui apenas para que fique completo. Acho muito conveniente para obter diferenças de (aninhados) objetos em geral.
import deepdiff
from pprint import pprint

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

Resultado:

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

Nota:

  • deepdiff o pacote precisa ser instalado como este não é um pacote padrão

  • Algum esforço terá que ser colocado para analisar o resultado


No entanto, por tomar as diferenças dos dicionários, acho que dictdiffer ser muito útil.
 5
Author: Anubis, 2018-06-18 14:54:44

Código

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

Teste

>>> a = {
    'number': 1,
    'list': ['one', 'two']
}
>>> b = {
    'list': ['two', 'one'],
    'number': 1
}
>>> equal(a, b)
True
 2
Author: Yas, 2017-02-04 21:12:41

Para testar se duas dicções são iguais em Chaves e valores:

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)

Se quiser devolver os valores que diferem, escreva de forma diferente:

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

Teria de lhe chamar duas vezes i. e.

dict1_minus_d2(d1,d2).extend(dict1_minus_d2(d2,d1))
 2
Author: simonltwick, 2018-01-02 21:33:36

A função é boa IMO, clara e intuitiva. Mas só para te dar (outra) resposta, aqui está a minha palavra:

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

Pode ser útil para si ou para qualquer outra pessoa..

 1
Author: zwep, 2018-05-02 09:30:43
>>> 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'
Aqui está outra opção:
>>> id(hash_1)
140640738806240
>>> id(hash_2)
140640738994848
Então, como vê, as duas identidades são diferentes. Mas os operadores de comparação ricos {[8] } parecem fazer o truque:
>>> 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
Author: Led Zeppelin, 2014-02-19 20:51:42
Em PyUnit há um método que compara os dicionários lindamente. Testei-o usando os dois dicionários seguintes, e faz exactamente o que procuras.
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))
Não recomendo a importação para o seu código de produção. Meu pensamento é a fonte em PyUnit poderia ser re-ferramenta para executar na produção. Ele usa {[[2]} que" imprime " os dicionários. Parece muito fácil adaptar este código para ser "produção pronta".
 0
Author: MikeyE, 2017-04-10 02:53:33

Em Python 3.6, pode ser feito como: -

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

Ret variable will be true if all the items of dict_1 in present in dict_2

 0
Author: Souravi Sinha, 2017-07-04 09:19:54

Ver objectos de vista do dicionário: https://docs.python.org/2/library/stdtypes.html#dict

Desta forma, poderá subtrair o dictView2 do dictView1 e ele irá devolver um conjunto de pares chave/valor que são diferentes no dictView2:

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

Você pode Intersectar, União, diferença( mostrada acima), diferença simétrica estes objetos de vista do dicionário.
Melhor? Mais rápido? - Não tenho a certeza, mas faz parte da biblioteca padrão - o que o torna um grande trunfo para a portabilidade

 0
Author: tranimatronic, 2017-08-09 16:33:09
import json

if json.dumps(dict1) == json.dumps(dict2):
    print("Equal")
 -5
Author: Mariusz K, 2016-04-02 14:14:04