Calcola la differenza nelle chiavi contenute in due dizionari Python


171

Supponiamo che io abbia due dizionari Python - dictAe dictB. Devo scoprire se ci sono chiavi presenti dictBma non presenti dictA. Qual è il modo più veloce per farlo?

Devo convertire le chiavi del dizionario in un set e poi procedere?

Interessato a conoscere i tuoi pensieri ...


Grazie per le tue risposte

Mi scuso per non aver affermato correttamente la mia domanda. Il mio scenario è il seguente: ne ho uno dictAche può essere uguale dictBo mancante di alcune chiavi rispetto a dictBoppure il valore di alcune chiavi potrebbe essere diverso e deve essere impostato su quello del dictAvalore della chiave.

Il problema è che il dizionario non ha standard e può avere valori che possono essere dettati.

Dire

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

Quindi il valore 'key2' deve essere ripristinato al nuovo valore e 'key13' deve essere aggiunto all'interno del dict. Il valore chiave non ha un formato fisso. Può essere un valore semplice o un dict o un dict di dict.

Risposte:


234

È possibile utilizzare le operazioni di impostazione sui tasti:

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

Ecco una classe per trovare tutte le possibilità: cosa è stato aggiunto, cosa è stato rimosso, quali coppie chiave-valore sono le stesse e quali coppie chiave-valore sono cambiate.

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

Ecco alcuni esempi di output:

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

Disponibile come repository github: https://github.com/hughdbrown/dictdiffer


3
Soluzione intelligente, grazie! L'ho fatto funzionare con i dadi annidati controllando se i valori modificati o invariati sono istanze dict e chiamando una funzione ricorsiva per controllarli di nuovo usando la tua classe.
AJJ,

1
@AJJ Mi piacerebbe vedere quell'implementazione.
urschrei

1
Che ne dici di un def update(self, new_dict): self.__init__(new_dict, self.current_dict)o simili in modo da poter fare un confronto a rotazione
Nick T

Alcune osservazioni: la DictDifferclasse è una classe senza stato e potrebbe essere una funzione. I valori changede unchangedpossono essere calcolati nello stesso loop. Queste due funzioni potrebbero restituire una listinvece di una setche è certamente meno costosa. Per un confronto approfondito, puoi dare un'occhiata al framework di unit test: docs.python.org/2/library/unittest.html , segui semplicemente il assertDictEqualmetodo nel codice sorgente.
Laurent LAPORTE il

1
FWIW, set(dictb)probabilmente è meglio di set(dictb.keys()).
mgilson,

60

Nel caso in cui desideri la differenza in modo ricorsivo, ho scritto un pacchetto per Python: https://github.com/seperman/deepdiff

Installazione

Installa da PyPi:

pip install deepdiff

Esempio di utilizzo

Importazione

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

Lo stesso oggetto restituisce vuoto

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

Il tipo di un elemento è cambiato

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

Il valore di un articolo è cambiato

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

Articolo aggiunto e / o rimosso

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

Differenza di stringa

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

Differenza di stringa 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

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

Elenco differenza

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

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

Elenca la differenza ignorando l'ordine o i duplicati: (con gli stessi dizionari di cui sopra)

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

Elenco che contiene il dizionario:

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

Imposta:

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

Tuple nominate:

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

Oggetti personalizzati:

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

Attributo oggetto aggiunto:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

Grazie per questo! Appena implementato nel mio progetto, funziona alla grande!
gtalarico,

1
@gtalarico Felice di aiutarti! Grazie per le gentili parole!
Seperman,

c'è qualche opzione per ignorare la differenza dell'ordine nell'elenco ? perché la mia applicazione non se ne preoccupa.
Lei Yang

Bel progetto, ha fatto tutto il lavoro con il minimo sforzo da parte mia. Grazie!
Stanislav Tsepa,

@LeiYang Sì, puoi impostare ignore_order=True. Puoi trovare i documenti su deepdiff.readthedocs.io/en/latest/diff.html
Seperman

18

non sono sicuro che sia "veloce" o no, ma normalmente si può fare questo

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

Devi scambiare dictae dictbdal momento che vuole sapere che quelle chiavi dictbnon ci sono dicta.
Gumbo,

2
for key in dicta.keys():=>for key in dicta:
Jean-François Fabre

15

Come ha scritto Alex Martelli, se vuoi semplicemente verificare se una chiave in B non è in A, any(True for k in dictB if k not in dictA)sarebbe la strada da percorrere.

Per trovare le chiavi mancanti:

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

Quindi queste due soluzioni hanno praticamente la stessa velocità.


8
Questo ha più senso:any(k not in dictA for k in dictB)
hughdbrown,

13

Se intendi davvero esattamente quello che dici (che devi solo scoprire SE "ci sono delle chiavi" in B e non in A, non QUALI SONO quelli che potrebbero esserlo), il modo più veloce dovrebbe essere:

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

Se in realtà hai bisogno di scoprire QUALI CHIAVI, se presenti, sono in B e non in A, e non solo "SE" ci sono tali chiavi, allora le risposte esistenti sono abbastanza appropriate (ma suggerisco più precisione nelle domande future se questo è anzi cosa intendi ;-).



8

La risposta migliore di hughdbrown suggerisce l'uso della differenza impostata, che è sicuramente l'approccio migliore:

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

Il problema con questo codice è che crea due elenchi solo per creare due set, quindi sta sprecando tempo 4N e spazio 2N. È anche un po 'più complicato di quanto debba essere.

Di solito, questo non è un grosso problema, ma se lo è:

diff = dictb.keys() - dicta

Python 2

In Python 2, keys()restituisce un elenco di chiavi, non a KeysView. Quindi devi chiedere viewkeys()direttamente.

diff = dictb.viewkeys() - dicta

Per il codice 2.7 / 3.x doppia versione, si spera che si stia utilizzando sixo qualcosa di simile, quindi è possibile utilizzare six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

In 2.4-2.6, non c'è KeysView. Ma puoi almeno tagliare il costo da 4N a N costruendo il tuo set di sinistra direttamente da un iteratore, invece di creare prima un elenco:

diff = set(dictb) - dicta

Elementi

Ho un dictA che può essere uguale a dictB o che potrebbe mancare alcune chiavi rispetto a dictB oppure il valore di alcune chiavi potrebbe essere diverso

Quindi non è davvero necessario confrontare le chiavi, ma gli elementi. An ItemsViewè solo Setse i valori sono hash, come le stringhe. Se lo sono, è facile:

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

Diff. Ricorsiva

Sebbene la domanda non richieda direttamente un diff ricorsivo, alcuni dei valori di esempio sono dicts e sembra che l'output atteso li differenzi ricorsivamente. Ci sono già più risposte qui che mostrano come farlo.


la risposta definitiva del 2018.
Jean-François Fabre

@ Jean-FrançoisFabre Naturalmente il materiale Python 2.4-2.6 è già abbastanza irrilevante nel 2018 ...
abarnert

alcune persone sono bloccate con 2.6
Jean-François Fabre


3

Ecco un modo che funzionerà, consentirà di valutare le chiavi Falsee, se possibile, utilizzerà comunque un'espressione del generatore. Non è eccezionalmente carino però.

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

MODIFICARE:

THC4k ha pubblicato una risposta al mio commento su un'altra risposta. Ecco un modo migliore e più carino per fare quanto sopra:

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

Non sono sicuro di come non mi sia mai passato per la testa ...


questa è la stessa risposta della precedente risposta di Alex Martelli
Jean-François Fabre

È adesso. Quando l'ho pubblicato (nove anni fa, lol) la risposta precedente era any(k for k in dictB if k not in dictA)che non era la stessa cosa (per le chiavi di falso). Controlla la cronologia delle modifiche / i timestamp.
Steve Losh,

3

Questa è una vecchia domanda e fa un po 'meno di quello di cui avevo bisogno, quindi questa risposta risolve davvero più di quanto questa domanda faccia. Le risposte a questa domanda mi hanno aiutato a risolvere quanto segue:

  1. (richiesto) Registra le differenze tra due dizionari
  2. Unisci le differenze dal n. 1 al dizionario di base
  3. (chiesto) Unisci le differenze tra due dizionari (tratta il dizionario n. 2 come se fosse un dizionario diff)
  4. Prova a rilevare i movimenti degli oggetti e le modifiche
  5. (chiesto) Fai tutto questo in modo ricorsivo

Tutto ciò combinato con JSON offre un supporto di archiviazione della configurazione piuttosto potente.

La soluzione ( anche su 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

2

che dire standart (confronta FULL Object)

PyDev-> nuovo modulo PyDev-> Modulo: 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()

Questo è meraviglioso se hai un enorme dizionario nidificato e vuoi confrontare ogni cosa all'interno e vedere le differenze. Grazie!
Matthew Moisen,

2

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

1

Ecco una soluzione per il confronto approfondito di 2 chiavi dei dizionari:

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

1

ecco una soluzione che può confrontare più di due 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

esempio di utilizzo:

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

1

La mia ricetta di differenza simmetrica tra due dizionari:

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 il risultato è:

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

1

Come menzionato in altre risposte, unittest produce dei buoni risultati per il confronto dei dicts, ma in questo esempio non vogliamo prima costruire un intero test.

Scartando la fonte unittest, sembra che tu possa ottenere una soluzione equa proprio con questo:

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

così

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)

Risultati in:

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

+  4: 111}
?        ^

-  5: 110}

Dove:

  • '-' indica chiave / valori nel primo ma non secondo dict
  • '+' indica chiave / valori nel secondo ma non nel primo dict

Come in unittest, l'unico avvertimento è che la mappatura finale può essere considerata una diff, a causa della virgola / parentesi finale.


1

@Maxx ha una risposta eccellente, usa gli unitteststrumenti forniti da 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)

Quindi, ovunque nel tuo codice puoi chiamare:

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

L'output risultante assomiglia all'output diff, stampando graziosamente i dizionari +o -anteponendo ogni riga diversa.


0

Non sono sicuro che sia ancora pertinente, ma ho riscontrato questo problema, la mia situazione avevo solo bisogno di restituire un dizionario delle modifiche per tutti i dizionari nidificati ecc. Ecc. Impossibile trovare una buona soluzione là fuori ma ho finito per scrivere una semplice funzione per fare questo . Spero che questo ti aiuti,


2
Sarebbe preferibile avere la minima quantità di codice che risolve effettivamente il problema del PO nella risposta, anziché un collegamento. Se il collegamento si interrompe o si sposta, la tua risposta diventa inutile.
George Stocker,

0

Se vuoi una soluzione integrata per un confronto completo con strutture di dict arbitrarie, la risposta di @ Maxx è un buon inizio.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)

Apparentemente non puoi istanziare una classe di test come quella, il che è troppo male.
Ben Liyanage,

0

Basato sulla risposta di ghostdog74,

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

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

stamperà diverso valore di dicta


0

Prova questo per trovare de intersezione, le chiavi che si trovano in entrambe le dizionari, se vuoi che le chiavi non si trovino nella seconda dizione, usa semplicemente il non in ...

intersect = filter(lambda x, dictB=dictB.keys(): x in dictB, dictA.keys())
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.