2010-08-04 17 views
8

Lavorare con dicts pitone profondamente nidificate, vorrei essere in grado di assegnare valori in una tale struttura di dati in questo modo:chiavi del dizionario Generazione al volo

mydict[key][subkey][subkey2]="value" 

senza dover controllare che mydict [tasto] ecc sono in realtà impostato come un dettato, ad es utilizzando

if not key in mydict: mydict[key]={} 

La creazione di subdiction dovrebbe avvenire al volo. Qual è il modo più elegante per consentire qualcosa di equivalente - magari usando decoratori sullo standard <type 'dict'>?

+0

Domanda correlata: http://stackoverflow.com/questions/3122566/in-a-python-dict-of-dicts-how-do-you-emulate-perls-auto-vivification-behavior/3122575#3122575 – unutbu

risposta

19
class D(dict): 
    def __missing__(self, key): 
     self[key] = D() 
     return self[key] 

d = D() 
d['a']['b']['c'] = 3 
+1

Devi stare attento con questo. 'd ['a'] = 2 d ['a'] ['b'] = 2' fallirà. – unholysampler

+0

Sì, ma ciò è implicito nella mia domanda: sto chiedendo un tipo misto di dizione e valori. – relet

+0

Per risolvere il caso d ['a'] = 2 d ['a'] ['b'] = 2, scriverei anche la classe D (dict): def __missing __ (self, key): value = self [tasto] = tipo di (auto)() valore di ritorno def __getitem __ (self, key): valori = dict .__ getitem __ (self, key) se isinstance (valori, dict): valori = SafeDict (valori) se isinstance (valori, le liste): per I, v in enumerate (valori): se isinstance (v, dict): valori [i] = SafeDict (v) i valori ritorno – user2346922

9

si potrebbe usare una tupla come chiave per il dict e allora non devi preoccuparti di subdictionaries affatto:

mydict[(key,subkey,subkey2)] = "value" 

In alternativa, se si ha realmente bisogno di avere subdictionaries per qualche motivo si potrebbe utilizzare collections.defaultdict.

Per due livelli questo è semplice:

>>> from collections import defaultdict 
>>> d = defaultdict(dict) 
>>> d['key']['subkey'] = 'value' 
>>> d['key']['subkey'] 
'value' 

per tre è un po 'più complessa:

>>> d = defaultdict(lambda: defaultdict(dict)) 
>>> d['key']['subkey']['subkey2'] = 'value' 
>>> d['key']['subkey']['subkey2'] 
'value' 

Quattro e più livelli sono lasciati come esercizio per il lettore. :-)

+0

Stupefacente. Sono contento di averlo chiesto, se non altro perché non capisco come potrei perdere questo. :) – relet

+0

Buona risposta. Puoi fornire come farlo con un 'defaultdict'? Annidare una volta è semplice: 'mydict = defaultdict (dict)'. Ma esiste una soluzione elegante per la nidificazione due volte? –

+0

@jellybean - ha appena aggiunto la soluzione a tre livelli; non è male –

2

Mi piace la risposta di Dave meglio, ma ecco un'alternativa.

from collections import defaultdict 
d = defaultdict(lambda : defaultdict(int)) 
>>> d['a']['b'] += 1 
>>> d 
defaultdict(<function <lambda> at 0x652f0>, {'a': defaultdict(<type 'int'>, {'b': 1})}) 
>>> d['a']['b'] 
1 

http://tumble.philadams.net/post/85269428/python-nested-defaultdicts

E 'sicuramente non abbastanza di dover utilizzare lambda per implementa le collezioni in default interne, ma a quanto pare necessaria.

+2

Lambdas non è mai necessario: puoi sempre usare una funzione con nome. In questo caso, l'uso di una funzione denominata significherebbe almeno che 'repr' ha qualcosa di più significativo di' lambda'. – Duncan