2014-07-22 9 views
7

Come possiamo fare l'accesso ricorsivo di detti nidificati, nel caso generale?Modo Pythonic di accesso a dict annidato in modo ricorsivo

from collections import defaultdict 

D = lambda: defaultdict(D) 
d = D() 

keys = ['k1', 'k2', 'k3'] 
value = 'v' 

if len(keys) == 3: 
    k1, k2, k3 = keys 
    d[k1][k2][k3] = value 
else: 
    ??? 

stavo giù qualche percorso orribile reduce, d.__getitem__ e d.__setitem__ ma sentito ci deve essere sicuramente un modo più elegante ...

+0

Buona ricerca @Dave. La tua ricerca è meglio della ricerca SO di domande correlate. – Gerrat

+0

In effetti una buona scoperta. Chiudere la mia stessa domanda come duplicato ... :) – wim

risposta

3

E 'abbastanza brutto, ma questo funziona:

def set_val(d, keys, val): 
    reduce(lambda x,y: x[y], keys[:-1], d)[keys[-1]] = val 

versione leggermente più leggibile:

def set_val(d, keys, val): 
    last = keys[-1] # Key we want to set val on 
    search_keys = keys[:-1] # Keys we need to traverse 
    reduce(lambda x,y: x[y], search_keys, d)[last] = val 

Uso:

>>> from collections import defaultdict 
>>> D = lambda: defaultdict(D) 
>>> d = D() 
>>> set_val(d, ['k1', 'k2', 'k3'], "hi") 
>>> d 
defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k1': defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k2': defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k3': 'hi'})})}) 
>>> d['k1']['k2']['k3'] 
'hi' 

utilizza reduce per raggiungere il più interno dict chiesto (keys[:-1]), quindi imposta la chiave finale nella lista al valore desiderato (output_of_reduce[keys[-1]] = val).

Si noti che in Python 3 è necessario un numero da functools import reduce per utilizzare questo.

Ecco il codice esteso per chiarezza:

def set_val(d, keys, val): 
    out = d 
    for k in keys[:-1]: 
     out = out[k] 
    out[keys[-1]] = val 
+0

questo funziona, ma spero che ci sia un'idea più elegante .. – wim

2

Si potrebbe utilizzare la ricorsione. Non si può dire che sia più elegante o più pitone del looping, o comunque usando la riduzione.

def assign(dct, keylist, value): 
    if not keylist: 
     dct = value 
    else: 
     dct[keylist[0]] = assign(dct[keylist[0]], keylist[1:], value) 
    return dct 


if __name__ == '__main__': 
    from collections import defaultdict 
    D = lambda: defaultdict(D) 
    d = D() 
    keys = ['k1', 'k2', 'k3'] 
    value = 'v' 
    assign(d, keys, value) 
    print d['k1']['k2']['k3'] 

[prints] 'v' 
Problemi correlati