Hitung perbedaan dalam kunci yang terkandung dalam dua kamus Python

171

Misalkan saya memiliki dua kamus Python - dictAdan dictB. Saya perlu mencari tahu apakah ada kunci yang ada dictBtetapi tidak di dictA. Apa cara tercepat untuk melakukannya?

Haruskah saya mengubah kunci kamus menjadi satu set dan kemudian melanjutkan?

Tertarik mengetahui pemikiran Anda ...


Terima kasih atas tanggapan Anda.

Permintaan maaf karena tidak menyatakan pertanyaan saya dengan benar. Skenario saya adalah seperti ini - Saya memiliki dictAyang dapat sama dictBatau mungkin memiliki beberapa kunci yang hilang dibandingkan dengan dictBatau nilai beberapa kunci mungkin berbeda yang harus ditetapkan dengan dictAnilai kunci.

Masalahnya adalah kamus tidak memiliki standar dan dapat memiliki nilai yang dapat berupa dikt.

Mengatakan

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

Jadi nilai 'key2' harus diatur ulang ke nilai baru dan 'key13' harus ditambahkan di dalam dikt. Nilai kunci tidak memiliki format tetap. Ini bisa berupa nilai sederhana atau dict atau dict.

Nathan Davis
sumber

Jawaban:

234

Anda dapat menggunakan operasi yang diatur pada tombol:

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

Berikut adalah kelas untuk menemukan semua kemungkinan: apa yang ditambahkan, apa yang telah dihapus, pasangan nilai kunci mana yang sama, dan pasangan nilai kunci mana yang diubah.

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

Berikut adalah beberapa contoh keluaran:

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

Tersedia sebagai repo github: https://github.com/hughdbrown/dictdiffer

hughdbrown
sumber
3
Solusi cerdas, terima kasih! Saya telah membuatnya bekerja dengan dict bersarang dengan memeriksa apakah nilai yang diubah atau tidak berubah adalah contoh dict dan memanggil fungsi rekursif untuk memeriksanya lagi menggunakan kelas Anda.
AJJ
1
@ AJJ Saya ingin melihat implementasi itu.
urschrei
1
Bagaimana def update(self, new_dict): self.__init__(new_dict, self.current_dict)dengan sejenisnya sehingga Anda dapat melakukan perbandingan bergulir
Nick T
Beberapa komentar: DictDifferkelas adalah kelas tanpa kewarganegaraan dan bisa menjadi fungsi. Nilai changeddan unchangeddapat dihitung dalam loop yang sama. Kedua fungsi ini bisa mengembalikan yang listbukan setyang tentu saja lebih murah. Untuk perbandingan mendalam, Anda dapat melihat kerangka pengujian Unit: docs.python.org/2/library/unittest.html , cukup ikuti assertDictEqualmetode dalam kode sumber.
Laurent LAPORTE
1
FWIW, set(dictb)mungkin lebih baik daripada set(dictb.keys()).
mgilson
60

Jika Anda ingin perbedaannya secara berulang, saya telah menulis paket untuk python: https://github.com/seperman/deepdiff

Instalasi

Instal dari PyPi:

pip install deepdiff

Contoh penggunaan

Pengimporan

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

Objek yang sama kembali kosong

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

Jenis item telah berubah

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

Nilai suatu barang telah berubah

>>> 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 ditambahkan dan / atau dihapus

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

Perbedaan string

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

Perbedaan 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

Ketik perubahan

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

Daftar perbedaan

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

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

Daftar perbedaan dengan mengabaikan urutan atau duplikat: (dengan kamus yang sama seperti di atas)

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

Daftar yang berisi kamus:

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

Set:

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

Dinamai Tuples:

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

Objek khusus:

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

Atribut objek ditambahkan:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
Seperman
sumber
Terima kasih untuk ini! Baru diimplementasikan pada proyek saya, berhasil!
gtalarico
1
@gtalarico Senang membantu! Terima kasih atas kata-kata baiknya!
Seperman
apakah ada opsi untuk mengabaikan perbedaan urutan daftar ? karena dosen aplikasi saya tidak peduli tentang hal itu.
Lei Yang
Proyek yang bagus, melakukan semua pekerjaan dengan usaha minimal dari pihak saya. Terima kasih!
Stanislav Tsepa
@LeiYang Ya, Anda dapat mengatur ignore_order=True. Anda dapat menemukan dokumen di deepdiff.readthedocs.io/en/latest/diff.html
Seperman
18

tidak yakin apakah itu "cepat" atau tidak, tetapi biasanya, orang bisa melakukan ini

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
sumber
Anda harus bertukar dictadan dictbkarena dia ingin tahu kunci-kunci dictbyang tidak masuk dicta.
Gumbo
2
for key in dicta.keys():=>for key in dicta:
Jean-François Fabre
15

Seperti yang ditulis Alex Martelli, jika Anda hanya ingin memeriksa apakah ada kunci dalam B tidak dalam A, any(True for k in dictB if k not in dictA)akan menjadi cara untuk pergi.

Untuk menemukan kunci yang hilang:

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

Jadi kedua solusi itu memiliki kecepatan yang hampir sama.

Jochen Ritzel
sumber
8
Ini lebih masuk akal:any(k not in dictA for k in dictB)
hughdbrown
13

Jika Anda benar-benar memaksudkan apa yang Anda katakan (bahwa Anda hanya perlu mencari tahu JIKA "ada kunci" dalam B dan tidak dalam A, bukan YANG MENGHASILKAN jika ada), cara tercepat adalah:

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

Jika Anda benar-benar perlu mencari tahu KUNCI YANG, jika ada, dalam B dan tidak dalam A, dan bukan hanya "JIKA" ada kunci seperti itu, maka jawaban yang ada cukup tepat (tapi saya sarankan lebih presisi dalam pertanyaan di masa depan jika itu memang apa yang Anda maksud ;-).

Alex Martelli
sumber
8

Gunakanset() :

set(dictA.keys()).intersection(dictB.keys())
Joziel Costa
sumber
set(d)sudah mengembalikan hanya kunci, sehingga Anda dapat melakukannyaset(da).intersection(db)
angka
8

Jawaban teratas oleh hughdbrown menyarankan menggunakan perbedaan set, yang jelas merupakan pendekatan terbaik:

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

Masalah dengan kode ini adalah bahwa ia membangun dua daftar hanya untuk membuat dua set, sehingga membuang waktu 4N dan ruang 2N. Ini juga sedikit lebih rumit dari yang seharusnya.

Biasanya, ini bukan masalah besar, tetapi jika:

diff = dictb.keys() - dicta

Python 2

Dalam Python 2, keys()mengembalikan daftar kunci, bukan a KeysView. Jadi, Anda harus meminta viewkeys()secara langsung.

diff = dictb.viewkeys() - dicta

Untuk kode dual-versi 2.7 / 3.x, mudah-mudahan Anda menggunakan sixatau sesuatu yang serupa, sehingga Anda dapat menggunakan six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

Di 2.4-2.6, tidak ada KeysView. Tetapi Anda setidaknya dapat memotong biaya dari 4N ke N dengan membangun set kiri Anda langsung dari iterator, daripada membangun daftar terlebih dahulu:

diff = set(dictb) - dicta

Item

Saya memiliki dictA yang bisa sama dengan dictB atau mungkin ada beberapa kunci yang hilang dibandingkan dengan dictB atau nilai beberapa kunci mungkin berbeda

Jadi Anda benar-benar tidak perlu membandingkan kunci, tetapi item. An ItemsViewhanya a Setjika nilainya hashable, seperti string. Jika ya, mudah:

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

Perbedaan rekursif

Meskipun pertanyaannya tidak secara langsung menanyakan perbedaan rekursif, beberapa nilai contohnya adalah dicts, dan tampaknya output yang diharapkan memang berbeda secara rekursif. Sudah ada beberapa jawaban di sini yang menunjukkan cara melakukannya.

abarnert
sumber
jawaban pasti dari 2018.
Jean-François Fabre
@ Jean-FrançoisFabre Tentu saja hal-hal Python 2.4-2.6 sudah cukup tidak relevan pada tahun 2018 ...
abarnert
beberapa orang terjebak dengan 2.6
Jean-François Fabre
3

Inilah cara yang akan bekerja, memungkinkan kunci yang dievaluasi False, dan masih menggunakan ekspresi generator untuk keluar lebih awal jika memungkinkan. Ini tidak terlalu cantik.

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

EDIT:

THC4k mengirim balasan ke komentar saya pada jawaban lain. Inilah cara yang lebih baik dan lebih cantik untuk melakukan hal di atas:

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

Tidak yakin bagaimana itu tidak pernah terlintas dalam pikiran saya ...

Steve Losh
sumber
ini adalah jawaban yang sama dengan jawaban Alex Martelli sebelumnya
Jean-François Fabre
Sekarang. Ketika saya mempostingnya (sembilan tahun yang lalu, lol) jawaban sebelumnya adalah any(k for k in dictB if k not in dictA)yang bukan hal yang sama (untuk kunci falsey). Periksa riwayat edit / cap waktu.
Steve Losh
3

Ini adalah pertanyaan lama dan meminta sedikit lebih sedikit dari yang saya butuhkan sehingga jawaban ini benar-benar memecahkan lebih dari pertanyaan ini. Jawaban dalam pertanyaan ini membantu saya memecahkan yang berikut:

  1. (ditanya) Catat perbedaan antara dua kamus
  2. Gabungkan perbedaan dari # 1 ke dalam kamus dasar
  3. (ditanya) Gabungkan perbedaan antara dua kamus (perlakukan kamus # 2 seolah-olah kamus yang berbeda)
  4. Cobalah untuk mendeteksi pergerakan item serta perubahan
  5. (bertanya) Lakukan semua ini secara rekursif

Semua ini dikombinasikan dengan JSON membuat dukungan penyimpanan konfigurasi yang cukup kuat.

Solusinya ( juga di 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
sumber
2

bagaimana dengan standar (bandingkan Objek LENGKAP)

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()
Maks
sumber
Ini luar biasa jika Anda memiliki kamus bersarang yang besar dan Anda ingin membandingkan setiap hal di dalamnya dan melihat perbedaannya. Terima kasih!
Matthew Moisen
2

Jika menggunakan 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
sumber
1

Berikut adalah solusi untuk membandingkan 2 kunci kamus:

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

inilah solusi yang dapat membandingkan lebih dari dua dicts:

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

contoh penggunaan:

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

Resep saya tentang perbedaan simetris antara dua kamus:

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)

Dan hasilnya adalah:

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

Seperti disebutkan dalam jawaban lain, unittest menghasilkan beberapa output yang bagus untuk membandingkan dicts, tetapi dalam contoh ini kita tidak ingin harus membangun seluruh tes terlebih dahulu.

Menggores sumber yang tidak terdaftar, sepertinya Anda bisa mendapatkan solusi yang adil hanya dengan ini:

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

begitu

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)

Hasil dalam:

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

+  4: 111}
?        ^

-  5: 110}

Dimana:

  • '-' menunjukkan kunci / nilai di dict pertama tetapi tidak kedua
  • '+' menunjukkan kunci / nilai di detik tetapi bukan dict pertama

Seperti di unittest, satu-satunya peringatan adalah bahwa pemetaan akhir dapat dianggap berbeda, karena koma / braket trailing.

Ryan de Kleer
sumber
1

@ Maxx memiliki jawaban yang sangat baik, gunakan unittestalat yang disediakan oleh 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)

Lalu, di mana pun dalam kode Anda, Anda dapat menelepon:

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

Output yang dihasilkan terlihat seperti output dari diff, cukup mencetak kamus dengan +atau -menambahkan setiap baris yang berbeda.

Brent Washburne
sumber
0

Tidak yakin apakah itu masih relevan tetapi saya menemukan masalah ini, situasi saya, saya hanya perlu mengembalikan kamus kamus untuk semua kamus bersarang dll. Tidak dapat menemukan solusi yang baik di luar sana tetapi pada akhirnya saya menulis fungsi sederhana untuk melakukan ini . Semoga ini membantu,

Jonathan Mickle
sumber
2
Akan lebih baik untuk memiliki jumlah kode terkecil yang memperbaiki masalah OP sebenarnya dalam jawaban, daripada tautan. Jika tautan mati atau bergerak, jawaban Anda menjadi tidak berguna.
George Stocker
0

Jika Anda menginginkan solusi bawaan untuk perbandingan penuh dengan struktur dikt sembarang, jawaban @ Maxx adalah awal yang baik.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)
Brad
sumber
Anda tampaknya tidak dapat membuat contoh kelas tes seperti itu, yang terlalu buruk.
Ben Liyanage
0

Berdasarkan jawaban ghostdog74,

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

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

akan mencetak nilai dicta yang berbeda

Pengguna normal
sumber
0

Coba ini untuk menemukan de persimpangan, kunci yang ada di kedua dictionarie, jika Anda ingin tombol tidak ditemukan di dictionarie kedua, cukup gunakan tidak di ...

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