2017-08-25 1 views
2

Ich habe ein wenig Mühe beim Vergleich zweier ähnlicher Wörterbücher. Ich möchte einen strengeren Vergleich der Werte (und wahrscheinlich der Schlüssel).Strict Vergleich der Wörterbücher in Python

Hier ist das wirklich grundlegende Problem:

>>> {'a': True} == {'a': 1} 
True 

ähnlich (und etwas verwirrend):

>>> {1: 'a'} == {True: 'a'} 
True 

Dies macht Sinn, weil True == 1. Was ich suche, ist etwas, das sich eher wie is verhält, aber zwei möglicherweise verschachtelte Wörterbücher vergleicht. Natürlich können Sie nicht verwenden is für die beiden Wörterbücher, denn das wird immer False zurückgeben, auch wenn alle Elemente identisch sind.

Meine aktuelle Lösung ist, einfach zu verwenden, um eine Zeichenfolgendarstellung beider zu erhalten und das zu vergleichen.

>>> json.dumps({'a': True}, sort_keys=True) == json.dumps({'a': 1}, sort_keys=True) 
False 

Aber das funktioniert nur, wenn alles JSON-serialisierbar ist.

Ich versuchte auch manuell alle Schlüssel und Werte zu vergleichen:

>>> l = {'a': True} 
>>> r = {'a': 1} 
>>> r.keys() == l.keys() and all(l[key] is r[key] for key in l.keys()) 
False 

Aber das schlägt fehl, wenn die Wörterbücher eine verschachtelte Struktur haben. Ich dachte, ich könnte eine rekursive Version davon schreiben, um den verschachtelten Fall zu behandeln, aber es schien unnötig hässlich und unpythonisch.

Gibt es einen "Standard" oder eine einfache Möglichkeit, dies zu tun?

Danke!

+4

Dies scheint nicht eine gute Idee - kleine ganze Zahlen und Strings, die gültige Bezeichner sind, vergleichen Sie OK mit 'ist' weil sie interniert sind (zumindest in CPython), aber Sie werden schnell Probleme mit allem Nicht-Trivialen haben. Was ist das zugrunde liegende Problem, das Sie zu lösen versucht haben, das Sie zu "True" vs. "1" als Wörterbuchschlüssel geführt hat? – jonrsharpe

+0

einige Beispielwörterbücher mit den verschachtelten Fällen, die Sie finden möchten, wäre nett – Alter

+0

Ich habe ein 'JSONField' in Django, das einige denormalisierte Daten speichert. (Es ist Produktdaten). Mit '{" assembly_required ": True}' wird klar, dass das Produkt assembliert werden muss, aber '{" assembly_required ": 1}' ist etwas weniger klar. DeepDiff sieht aus wie es funktioniert. Müsste nur einen 'DeepDiff (links, rechts) == {}' machen. –

Antwort

2

Sie waren ziemlich nah mit JSON: Verwenden Sie stattdessen Pythons pprint Modul. Dies wird dokumentiert Wörterbücher sortieren in Python 2.5+ und 3:

Wörterbücher werden durch Schlüssel sortiert, bevor die Anzeige berechnet wird.

Lassen Sie uns dies bestätigen. Hier ist eine Sitzung in Python 3.6 (die bequem bewahrt Auftrag auch für die regelmäßigen dict Objekte):

Python 3.6.2 (v3.6.2:5fd33b5, Jul 8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)] 
on win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> a = {2: 'two', 3: 'three', 1: 'one'} 
>>> b = {3: 'three', 2: 'two', 1: 'one'} 
>>> a 
{2: 'two', 3: 'three', 1: 'one'} 
>>> b 
{3: 'three', 2: 'two', 1: 'one'} 
>>> a == b 
True 
>>> c = {2: 'two', True: 'one', 3: 'three'} 
>>> c 
{2: 'two', True: 'one', 3: 'three'} 
>>> a == b == c 
True 
>>> from pprint import pformat 
>>> pformat(a) 
"{1: 'one', 2: 'two', 3: 'three'}" 
>>> pformat(b) 
"{1: 'one', 2: 'two', 3: 'three'}" 
>>> pformat(c) 
"{True: 'one', 2: 'two', 3: 'three'}" 
>>> pformat(a) == pformat(b) 
True 
>>> pformat(a) == pformat(c) 
False 
>>> 

Und lassen Sie sie schnell, dass ziemlich Druck bestätigen sortiert verschachtelte Wörterbücher:

>>> a['b'] = b 
>>> a 
{2: 'two', 3: 'three', 1: 'one', 'b': {3: 'three', 2: 'two', 1: 'one'}} 
>>> pformat(a) 
"{1: 'one', 2: 'two', 3: 'three', 'b': {1: 'one', 2: 'two', 3: 'three'}}" 
>>> 

Also, statt zu JSON von Serialisierung Serialisierung mit pprint.pformat(). Ich stelle mir vor, dass es einige Fälle gibt, in denen zwei Objekte, die Sie als ungleich betrachten möchten, dennoch die gleiche hübsch gedruckte Darstellung erzeugen. Aber diese Fälle sollten selten sein, und Sie wollten etwas Einfaches und Pythonic, was das ist.

0

Sie können isinstance() verwenden, um zwischen einem regulären Wörterbucheintrag und einem verschachtelten Wörterbucheintrag zu unterscheiden. Auf diese Weise können Sie is durchlaufen, um genau zu vergleichen, aber auch überprüfen, wenn Sie eine Ebene in das verschachtelte Wörterbuch tauchen müssen.

https://docs.python.org/3/library/functions.html#isinstance

myDict = {'a': True, 'b': False, 'c': {'a': True}} 
for key, value in myDict.items(): 
    if isinstance(value, dict): 
     # do what you need to do.... 
    else: 
     # etc... 
+0

'isinstance (meinDict, dict)': .. myDict -> myDict [x]? – Alter

+0

behoben. Danke, dass du – blake

+0

geholt hast. Ich denke nicht, dass das auch funktionieren wird, x ist nur der Schlüssel. Wahrscheinlich brauche myDict [x], um den Inhalt – Alter

0

Sie Identität aller (Schlüssel, Wert) testen können Paare elementweise:

def equal_dict(d1, d2): 
    return all((k1 is k2) and (v1 is v2) 
       for (k1, v1), (k2, v2) in zip(d1.items(), d2.items())) 

>>> equal_dict({True: 'a'}, {True: 'a'}) 
True 

>>> equal_dict({1: 'a'}, {True: 'a'}) 
False 

Dies sollte mit float arbeiten, int, str und bool, aber nicht andere Sequenzen oder komplexere Objekte. Wie auch immer, das ist ein Anfang, wenn Sie es brauchen.

+0

Das funktioniert nicht, weil Sie mit 'is' testen. Ihre Tests geben die gewünschten Ergebnisse aufgrund von Internierungen und möglicherweise anderen Python-Mikrooptimierungen. Es kann jedoch fälschlicherweise zwei gleiche Wörterbücher als ungleich melden. Verwenden Sie beispielsweise anstelle von "a" zwei separate Variablen und geben Sie diesen Variablen Werte gleich, aber nicht intern, wie z. B. große ganze Zahlen. –

+0

Und wie Sie bereits gesagt haben, funktioniert es nicht für "Sequenzen oder komplexere Objekte". Daher erfüllt es nicht die Anforderung von OP, dass es verschachtelte Strukturen behandelt. –

0

Ich denke, Sie suchen nach so etwas. Da Sie jedoch keine Beispieldaten zur Verfügung gestellt haben, werde ich nicht raten, was es sein könnte

from boltons.itertools import remap 

def compare(A, B): return A == B and type(A) == type(B) 

dict_to_compare_against = { some dict } 

def visit(path, key, value): 
    cur = dict_to_compare_against 
    for i in path: 
     cur = cur[i] 

    if not compare(cur, value): 
     raise Exception("Not equal") 


remap(other_dict, visit=visit) 
Verwandte Themen