2015-06-13 13 views
24

Prüfung auf Gleichheit funktioniert wie folgt für Python dicts fein:Testen Sie, ob dict in dict enthalten

first = {"one":"un", "two":"deux", "three":"trois"} 
second = {"one":"un", "two":"deux", "three":"trois"} 

print(first == second) # Result: True 

Aber jetzt meine zweite dict enthält einige zusätzliche Tasten ich ignorieren wollen:

first = {"one":"un", "two":"deux", "three":"trois"} 
second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"} 

Ist Gibt es eine einfache Möglichkeit zu testen, ob das erste Diktat Teil des zweiten Diktats ist, mit all seinen Schlüsseln und Werten?

EDIT 1:

Diese Frage vermutet wird ein Duplikat von How to test if a dictionary contains certain keys zu sein, aber ich habe Interesse an Testschlüssel und ihre Werte. Nur die gleichen Schlüssel zu enthalten, macht nicht zwei dicts gleich.

EDIT 2:

OK, ich habe einige Antworten jetzt vier verschiedene Methoden verwendet, und erwies sich als alle von ihnen arbeiten. Da ich einen schnellen Prozess brauche, habe ich jeden für die Ausführungszeit getestet. Ich erstellte drei identische dicts mit 1000 Elementen, Schlüssel und Werte waren zufällige Strings der Länge 10. Die second und third erhielten einige zusätzliche Schlüssel/Wert-Paare, und der letzte Nicht-Extraschlüssel der third bekam einen neuen Wert. So ist first eine Teilmenge von second, aber nicht von third. Mit Modul timeit mit 10000 Wiederholungen, ich habe:

Method              Time [s] 
first.viewitems() <=second.viewitems()       0.9 
set(first.items()).issubset(second.items())      7.3 
len(set(first.items()) & set(second.items())) == len(first)  8.5 
all(first[key] == second.get(key, sentinel) for key in first) 6.0 

Ich vermutete, die letzte Methode ist die langsamste, aber es ist auf Platz 2. Aber Methode 1 schlägt sie alle.

Vielen Dank für Ihre Antworten!

+1

möglich Duplikat von [Wie testen, ob ein Wörterbuch bestimmten Schlüssel enthält] (http://stackoverflow.com/questions/3415347/how-to-test-if-a-dictionary-contains-certain-keys) – tjati

Antwort

44

Sie können ein dictionary view verwenden:

# Python 2 
if first.viewitems() <= second.viewitems(): 
    # true only if `first` is a subset of `second` 

# Python 3 
if first.items() <= second.items(): 
    # true only if `first` is a subset of `second` 

Wörterbuch Ansichten sind die standard in Python 3, In Python 2 müssen Sie die Standardmethoden mit view voranstellen. Sie verhalten sich wie Mengen und <= testet, ob eine davon eine Untermenge von (oder ist gleich) anderen ist.

Demo in Python 3:

>>> first = {"one":"un", "two":"deux", "three":"trois"} 
>>> second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"} 
>>> first.items() <= second.items() 
True 
>>> first['four'] = 'quatre' 
>>> first.items() <= second.items() 
False 

Dies funktioniert für nicht-hashable Werte zu, da die Schlüssel bereits die Schlüssel-Wert-Paare einzigartig machen. Die Dokumentation ist ein wenig über diesen Punkt verwirrend, aber auch bei veränderlichen Werte (zum Beispiel Listen) das funktioniert:

>>> first_mutable = {'one': ['un', 'een', 'einz'], 'two': ['deux', 'twee', 'zwei']} 
>>> second_mutable = {'one': ['un', 'een', 'einz'], 'two': ['deux', 'twee', 'zwei'], 'three': ['trois', 'drie', 'drei']} 
>>> first_mutable.items() <= second_mutable.items() 
True 
>>> first_mutable['one'].append('ichi') 
>>> first_mutable.items() <= second_mutable.items() 
False 

Sie auch die all() function mit einem Generator Ausdruck verwenden könnte; verwenden object() als Sentinel fehlende Werte zu erkennen prägnant:

sentinel = object() 
if all(first[key] == second.get(key, sentinel) for key in first): 
    # true only if `first` is a subset of `second` 

aber das ist nicht so gut lesbar und ausdrucksstark wie mit Wörterbuch Ansichten.

4

Also, Sie wollen im Grunde überprüfen, ob ein Wörterbuch eine Untermenge eines anderen ist.

first = {"one":"un", "two":"deux", "three":"trois"} 
second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"} 

def subset_dic(subset, superset): 
    return len(set(subset.items()) & set(superset.items())) == len(subset) 


print(subset_dic(first, second)) 

Drucke:

True 

Falls Sie zu der Untergruppe/Oberteil abstrakt wollen:

def subset_dic(dict1, dict2): 
    return len(set(dict1.items()) & set(dict2.items())) == len(min((dict1, dict2), key=len)) 

Hinweis: Das wird nicht funktionieren, wenn jeder Wert ist ein veränderbares Objekt. Daher können Sie einen zusätzlichen Schritt (konvertierbares Objekt in unveränderbares Analog) in die Funktion einfügen, um diese Einschränkung zu überwinden.

+0

Erstellen eines Satzes von beiden vollständigen Wörterbüchern scheint ein bisschen teuer. – poke

+0

Der Vergleich zweier Sätze von Tupeln ohne Hashing ist in puncto Komplexität noch teurer. –

+0

Ja, aber keine Notwendigkeit, Tupel zu vergleichen; Wörterbücher haben bereits einen O (1) -Elementzugriff, so dass Sie einfach durch ein Wörterbuch iterieren und Mitgliederprüfungen für das andere durchführen können. – poke

6
all(k in second and second[k] == v for k, v in first.items()) 

, wenn Sie wissen, dass keiner der Werte None sein kann, um es zu vereinfachen:

all(second.get(k, None) == v for k, v in first.items()) 
+0

Warum haben Sie die alternative Version entfernt, die Sie zuvor gepostet haben? 'nicht (set (first.items()) - set (second.items()))' –

+0

@Iskren weil es nicht funktioniert, wenn Werte nicht hashbar sind, zum Beispiel wenn '' foo ': [1, 2, 3 ] war einer der Punkte. –

+1

Ihre zweite Lösung ist sehr elegant, obwohl es bei tief verschachtelten Werten langsam werden könnte. Trotzdem ist es sehr polymorph und prägnant. +1 –

2

# Aktualisiert Am:

METHODE-1: Mit Wörterbuch Aufrufe:

Als Martijn vorgeschlagen, Wir Wörterbuch Ansichten dies überprüfen verwenden können. dict.viewitems() wirkt als ein Satz. Wir können verschiedene Set-Operationen auf diesem wie Schnitt, Vereinigung usw. (diese link prüfen.) Durchführen

first.viewitems() <= second.viewitems() 
True 

Wir prüfen, ob first kleiner als gleich second. Diese Wahr-Auswerteeinrichtung first ist eine Teilmenge von second.

METHOD-2 Unter Verwendung IsSubset() Betrieb der Sets:

(Nutzungsbedingungen. Diese Methode gewisse Redundanz hat, und verlangt, dass alle Werte hashable sein Method -1 wird vorgeschlagen, gefolgt werden, um alle Fälle zu behandeln. Dank Martijn für die Anregungen.)

verwenden .items() Attribut eines Wörterbuchs eine Liste von (Schlüssel, Wert) Tupeln und dann IsSubset verwenden, um get() Betrieb von Sätzen.

Dies überprüft sowohl die Schlüssel als auch die Gleichheit..

>>> first = {"one":"un", "two":"deux", "three":"trois"} 
>>> second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"} 

>>> set(first.items()).issubset(second.items()) 
True 
+1

Warum zuerst 'list()' verwenden und 'set nicht verwenden (first.items()). Issubset (second.items())' direkt? Und in Python 3 unterstützt 'dict.items()' * den Usecase direkt *; 'l1.items()

+0

Danke Martijn. Dieser Schritt war unnötig. Aktualisiert die Ans! –

+0

Sie haben hier noch Redundanz; In Python 3 fungiert 'dict.items()' * bereits als Menge *. In Python 2 können Sie dasselbe Verhalten mit 'dict.viewitems()' erreichen. Und Ihr Ansatz erfordert immer noch, dass die Werte hashbar sind, während Dictionary-Ansichten dies nicht tun. –

Verwandte Themen