2015-06-13 15 views
24

Testowanie równości działa poprawnie tak dla dicts Pythonietestu jeśli DICT zawarte w dict

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

print(first == second) # Result: True 

Ale teraz mój drugi DICT zawiera kilka dodatkowych przycisków, które chcę ignorować:

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

Czy istnieje prosty sposób sprawdzenia, czy pierwszy dyktat jest częścią drugiego dyktatu, ze wszystkimi jego kluczami i wartościami?

EDIT 1:

To pytanie podejrzewa się duplikatem How to test if a dictionary contains certain keys, ale jestem zainteresowany przetestowaniem kluczy i ich wartości. Użycie tych samych kluczy nie oznacza, że ​​dwa dyktury są sobie równe.

EDIT 2:

OK, mam kilka odpowiedzi teraz za pomocą czterech różnych metod, a wszystkie z nich okazały się pracuje. Ponieważ potrzebuję szybkiego procesu, przetestowałem każdy na czas wykonania. Stworzyłem trzy identyczne dyktały z 1000 pozycji, klucze i wartości były losowymi ciągami o długości 10. Modele second i third otrzymały parę dodatkowych par klucz-wartość, a ostatni klucz dodatkowy w postaci third otrzymał nową wartość. Tak więc, first jest podzbiorem second, ale nie z third. Korzystanie z modułu timeit 10000 powtórzeń, mam:

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 

Domyślałem ostatnia metoda jest najwolniejsza, ale to na miejscu 2. Ale metoda 1 bije je wszystkie.

Dzięki za odpowiedzi!

+1

możliwe duplikat [Jak sprawdzić, czy słownik zawiera pewne przyciski] (http://stackoverflow.com/questions/3415347/how-to-test-if-a-dictionary-contains-certain-keys) – tjati

Odpowiedz

44

można użyć dictionary view:

# 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` 

słownik widoki są z standard in Python 3, w Pythonie 2 należy poprzedzić standardowe metody za pomocą view. Działają one jak zestawy i testy <=, jeśli jeden z nich jest podzbiorem (lub jest równy) innym.

Demo w Pythonie 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 

Działa to dla wartości non-hashable zbyt, jak klawisze sprawiają, że pary klucz-wartość już wyjątkowy. Dokumentacja jest trochę mylące w tej kwestii, ale nawet z modyfikowalnych wartości (powiedzmy, listy) to działa:

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

Można również użyć all() function z wyrażeniem generatora; używać object() jako strażnik w celu wykrycia brakujących wartości zwięźle:

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

ale to nie jest tak czytelny i wyrazisty, jak przy użyciu słownika widoki.

4

Więc, zasadniczo chcesz sprawdzić, czy jeden słownik jest podzbiorem innego.

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

Wydruki:

True 

W przypadku, gdy chcesz abstrakcyjnego się części podzbiór/rozszerzeniem:

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

Uwaga: to będzie nie działać, jeśli wartość jest obiekt zmienny. W związku z tym można dodać dodatkowy krok (przekształcić zmienny obiekt na niezmienny analog) w celu przezwyciężenia tego ograniczenia.

+0

Tworzenie zestawu obu pełnych słowników wydaje się nieco kosztowne. – poke

+0

Porównanie dwóch zestawów krotek bez mieszania jest nawet droższe pod względem samej złożoności. –

+0

Tak, ale nie ma potrzeby porównywania krotek; Słowniki mają już dostęp do O (1) pozycji, więc możesz po prostu przejrzeć jeden słownik i wykonać kontrole na drugim. – poke

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

jeśli wiesz, że żadna z wartości może być None, to uprościć do:

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

Dlaczego usunąłeś wcześniej opublikowaną wersję alternatywną? 'not (set (first.items()) - set (second.items()))' –

+0

@Iskren, ponieważ nie zadziała, jeśli wartości nie są zgodne, na przykład jeśli '' foo ': [1, 2, 3 ] 'był jedną z pozycji. –

+1

Twoje drugie rozwiązanie jest bardzo eleganckie, ale może się spowalniać, jeśli wartości są głęboko zagnieżdżone. Mimo to jest wysoce polimorficzna i zwięzła. +1 –

2

# Zaktualizowane Odp:

SPOSÓB 1: Używanie Słownik Views:

Jak Martijn sugerował, możemy użyć słownika widoki to sprawdzić. dict.viewitems() działa jako zestaw. Możemy wykonać różne ustawienia i operacje na tym skrzyżowaniu, jak unia itd. (Sprawdź to link.)

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

możemy sprawdzić, czy first jest mniejsza niż równa second. Ten oceniającemu oznacza prawdę first jest podzbiorem second.

Metoda 2 Stosując issubset() operacji zestawów:

(WYŁĄCZENIE. Metoda ta ma pewną nadmiarowość i wymaga, aby wszystkie wartości są hashable Metoda -1 sugeruje, których należy przestrzegać, aby obsłużyć wszystkie przypadki. Dzięki Martijn za sugestie.)

Zastosowanie .items() atrybut słownika, aby uzyskać listę (klucz, wartość) krotek, a następnie użyć issubset() obsługa zestawów.

Spowoduje to sprawdzenie zarówno kluczy, jak i równości..

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

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

Najpierw użyj 'list()' i nie używaj 'set (first.items()). Issubset (second.items())' bezpośrednio? A w Pythonie 3 'dict.items()' * obsługuje usecase bezpośrednio *; 'l1.items()

+0

Dzięki Martijn. Ten krok był niepotrzebny. Zaktualizowano ans! –

+0

Nadal masz tutaj nadmiarowość; w Pythonie 3 'dict.items()' * działa już jako zestaw *. W Pythonie 2 możesz uzyskać to samo zachowanie, używając 'dict.viewitems()'. A twoje podejście wciąż wymaga, aby wartości były nieosiągalne, podczas gdy widoki słownikowe nie. –