2012-02-16 11 views
13

Sto cercando di creare un semplice meccanismo di "ricerca" in python e volevo assicurarmi che non ci fosse già qualcosa da qualche parte nascosto nelle vaste librerie in python che non lo fanno già questo prima di crearlo.Accesso a python dict con una stringa di ricerca chiavi multiple

Sto cercando di prendere un dict che è formattato qualcosa di simile

my_dict = { 
    "root": { 
    "secondary": { 
     "user1": { 
      "name": "jim", 
      "age": 24 
     }, 
     "user2": { 
     "name": "fred", 
     "age": 25 
     } 
    } 
    } 
} 

e sto cercando di avere un modo per accedere ai dati utilizzando una notazione decimale che sarebbe qualcosa di simile a

root.secondary.user2 

e restituire tale risultato come risposta. Penso che ci sia qualcosa che fa questo e potrei scriverne uno senza troppe difficoltà, ma voglio assicurarmi di non ricreare qualcosa che potrebbe mancare nella documentazione. Grazie

+0

Sei alla ricerca di zucchero sintattico ('mydict.root.secondary.user2') o semplicemente ricerca tramite una stringa? –

risposta

28

Non c'è niente nella libreria standard per questo scopo, ma è piuttosto facile da codificare da soli:

>>> key = "root.secondary.user2" 
>>> reduce(dict.get, key.split("."), my_dict) 
{'age': 25, 'name': 'fred'} 

Questa sfrutta il fatto che il look-up per la chiave k in il dizionario d può essere scritto come dict.get(d, k). L'applicazione ripetuta di iterativamente utilizzando reduce() porta al risultato desiderato.

Edit: Per completezza tre funzioni per ottenere, impostare o eliminare chiavi del dizionario con questo metodo:

def get_key(my_dict, key): 
    return reduce(dict.get, key.split("."), my_dict) 

def set_key(my_dict, key, value): 
    key = key.split(".") 
    my_dict = reduce(dict.get, key[:-1], my_dict) 
    my_dict[key[-1]] = value 

def del_key(my_dict, key): 
    key = key.split(".") 
    my_dict = reduce(dict.get, key[:-1], my_dict) 
    del my_dict[key[-1]] 
+0

Questo funziona benissimo per il recupero, ma qualsiasi idea sull'aggiornamento di un campo in quel codice usando lo stesso metodo – Tanerax

+0

+1 per una risposta 'reduce()'; anche se ho anche solo la risposta, vorrei poter +2. – SingleNegationElimination

+0

@Tanerax: per aggiornare, suddividere l'ultimo componente della chiave e recuperare il dizionario contenente i componenti principali. Aggiunto codice di esempio alla risposta. –

1

ricorsione funziona ancora.

def walk_into(dict, key): 
    head, _, tail = key.partition('.') 
    if tail: 
     return walk_into(dict[head], tail) 
    return dict, key 
d, k = walk_into(my_dict, "root.secondary.user2") 

d[k] possono essere utilizzati per ottenere o mettere un nuovo valore.

2

Puoi averlo. È possibile sottoclasse dict, aggiungere la ricerca chiave (e persino mantenere il nome dettt) utilizzando un codice simile a quello sottostante. Il modulo {...} tuttavia utilizzerà comunque la classe di dict incorporata (ora denominata orig_dict), quindi è necessario includerlo, ad esempio: Dict({...}). Questa implementazione converte ricorsivamente i dizionari nel nuovo modulo, quindi non è necessario utilizzare il metodo sopra riportato per le voci del dizionario che sono semplici dizionari.

orig_dict = dict 
class Dict(orig_dict): 
    def __init__(self, *args, **kwargs): 
     super(Dict, self).__init__(*args, **kwargs) 
     for k, v in self.iteritems(): 
      if type(v) == orig_dict and not isinstance(v, Dict): 
       super(Dict, self).__setitem__(k, Dict(v)) 
    def __getattribute__(self, k): 
     try: return super(Dict, self).__getattribute__(k) 
     except: return self.__getitem__(k) 
    def __setattr__(self, k, v): 
     if self.has_key(k): self.__setitem__(k, v) 
     else: return super(Dict, self).__setattr__(k, v) 
    def __delattr__(self, k): 
     try: self.__delitem__(k) 
     except: super(Dict, self).__delattr__(k) 
    def __setitem__(self, k, v): 
     toconvert = type(v) == orig_dict and not isinstance(v, Dict) 
     super(Dict, self).__setitem__(k, Dict(v) if toconvert else v) 

# dict = Dict <-- you can even do this but I advise against it 

# testing: 
b = Dict(a=1, b=Dict(c=2, d=3)) 
c = Dict({'a': 1, 'b': {'c': 2, 'd': 3}}) 
d = Dict(a=1, b={'c': 2, 'd': {'e': 3, 'f': {'g': 4}}}) 

b.a = b.b 
b.b = 1 
d.b.d.f.g = 40 
del d.b.d.e 
d.b.c += d.b.d.f.g 
c.b.c += c.a 
del c.a 
print b 
print c 
print d 
Problemi correlati