2015-06-13 21 views
24

test per l'uguaglianza funziona bene come questo per dicts pitone:prova se dict contenute in dict

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

print(first == second) # Result: True 

Ma ora il mio secondo dict contiene alcuni tasti aggiuntivi che voglio ignorare:

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

Is c'è un modo semplice per verificare se il primo dict fa parte del secondo dict, con tutte le sue chiavi e valori?

EDIT 1:

Questa domanda è sospettato di essere un duplicato di How to test if a dictionary contains certain keys, ma io sono interessato a chiavi di test ei loro valori. Contenere solo le stesse chiavi non equivale a due dadi.

EDIT 2:

OK, ho avuto alcune risposte ora utilizzando quattro diversi metodi, e ha dimostrato tutti loro lavorano. Poiché ho bisogno di un processo veloce, ho testato ciascuno per il tempo di esecuzione. Ho creato tre dit identici con 1000 elementi, le chiavi e i valori erano stringhe casuali di lunghezza 10. Il second e third ha ottenuto alcune coppie di valori-chiave in più e l'ultima chiave non aggiuntiva dello third ha ottenuto un nuovo valore. Quindi, first è un sottoinsieme di second, ma non di third. Utilizzando il modulo timeit con 10000 ripetizioni, ho ottenuto:

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 

ho indovinato l'ultimo metodo è il più lento, ma è sul posto 2. Ma il metodo 1 li batte tutti.

Grazie per le vostre risposte!

+1

possibile duplicato di [Come verificare se un dizionario contiene alcuni tasti] (http://stackoverflow.com/questions/3415347/how-to-test-if-a-dictionary-contains-certain-keys) – tjati

risposta

44

È possibile utilizzare un 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` 

viste del dizionario sono la standard in Python 3, in Python 2 è necessario anteporre i metodi standard a view. Si comportano come insiemi e test <= se uno di questi è un sottoinsieme di (o è uguale a) un altro.

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 

Questo funziona per valori non troppo hashable, come le chiavi fanno le coppie di valori-chiave univoca già. La documentazione è un po 'di confusione su questo punto, ma anche con i valori mutevoli (ad esempio, le liste) questo funziona:

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

Si potrebbe anche usare il all() function con un generatore di espressione; utilizzare object() come una sentinella per rilevare i valori mancanti in modo conciso:

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

, ma questo non è così leggibile ed espressiva come con viste del dizionario.

4

Quindi, in pratica, si desidera verificare se un dizionario è un sottoinsieme di un altro.

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

Stampe:

True 

Nel caso in cui si desidera estratto la parte sottoinsieme/superset:

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

Nota: questo non funzionare, se qualsiasi valore è un oggetto mutevole. Quindi è possibile aggiungere un ulteriore passaggio (convertire l'oggetto mutabile in analogico immutabile) nella funzione per superare questa limitazione.

+0

Creare un set di entrambi i dizionari completi sembra un po 'costoso. – poke

+0

Confrontare due set di tuple senza hashing è ancora più costoso in termini di pura complessità. –

+0

Sì, ma non è necessario confrontare le tuple; i dizionari hanno già accesso alla voce O (1), quindi puoi semplicemente scorrere un dizionario e fare controlli sui membri dell'altro. – poke

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

se si sa che nessuno dei valori può essere None, che semplificherà a:

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

Perché hai rimosso la versione alternativa che hai postato in precedenza? 'not (set (first.items()) - set (second.items()))' –

+0

@Iskren perché non funzionerà se i valori non sono lavabili, ad esempio se ''foo': [1, 2, 3 ] 'era uno degli elementi. –

+1

La tua seconda soluzione è molto elegante, anche se potrebbe rallentare se i valori sono profondamente annidati. Tuttavia è altamente polimorfico e conciso. +1 –

2

# Ans Aggiornato:

METODO-1: utilizzando il dizionario Viste:

Come Martijn suggerito, possiamo usare viste del dizionario di controllare questo. dict.viewitems() funge da set. Siamo in grado di eseguire diverse operazioni di set su questo incrocio come, unione ecc (Controllare questo link.)

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

Verifichiamo se first è inferiore pari a second. Questa valutazione a mezzo Veri first è un sottoinsieme di second.

METODO-2 Uso issubset() funzionamento di insiemi:

(NOTA:. Questo metodo ha una certa ridondanza e richiede che tutti i valori di essere hashable Metodo -1 è suggerito da seguire per gestire tutti i casi. Grazie Martijn per i suggerimenti.)

Uso .items() attributo di un dizionario per ottenere un elenco di (chiave, valore) tuple e quindi utilizzare issubset() funzionamento di set.

Verificherà sia le chiavi che l'uguaglianza..

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

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

Perché usare 'list()' prima e non usare 'set (first.items()). Issubset (second.items())' direttamente? E in Python 3, 'dict.items()' * supporta direttamente l'usecase *; 'l1.items()

+0

Grazie Martijn. Quel passo non era necessario. Aggiornato gli ans! –

+0

Hai ancora ridondanza qui; in Python 3 'dict.items()' * funge già da set *. In Python 2 puoi ottenere lo stesso comportamento usando 'dict.viewitems()'. E il tuo approccio richiede ancora che i valori siano lavabili, mentre le viste del dizionario no. –