2012-09-13 16 views
11

Este me está volviendo loco. Dado el siguiente diccionario:Comprobando un diccionario usando una cadena de notación de puntos

d = {"a":{"b":{"c":"winning!"}}} 

tengo esta cadena (de una fuente externa, y no puedo cambiar esta metáfora).

k = "a.b.c" 

necesito para determinar si el diccionario tiene la llave 'c', así que puedes añadir si no lo hace.

Esto funciona de maravillas para recuperar un valor de notación de puntos:

reduce(dict.get, key.split("."), d) 

pero no puedo encontrar la manera de 'reducir' un cheque has_key ni nada de eso.

Mi problema principal es la siguiente: dado "a.b.c.d.e", Necesito crear todos los elementos necesarios en el diccionario, pero no pisar ellos si ya existen. Si alguien sabe cómo hacer todo eso, serás mi héroe.

+4

Sí, simplemente aceptar sus propias respuestas a sus preguntas. Eso está perfectamente bien (incluso deseado, para que las personas que tengan la misma pregunta verán que existe una respuesta). –

+0

Por casualidad, ¿la fuente es realmente Cocoa KVC? Y, si es así, ¿puedes usar PyObjC en lugar de Python puro? (Además de ser más simple, eso también garantizaría que las claves significan exactamente lo mismo para lo que están destinadas, incluso si hay errores en el código de otras personas ...) – abarnert

+0

¿Qué tiene de especial el uso de la notación de puntos 'como cadena' para buscar a través del diccionario? Uno tiene que usar alguna función adicional para ello. ¿Qué hay para buscar dicts multidimensionales estilo JS, sin ningún tipo de cadenas 'x = conf.a.b.c'. No sé si es posible en Python. Pero usar una cuerda no es genial – Green

Respuesta

9

... o el uso de la recursividad:

def put(d, keys, item): 
    if "." in keys: 
     key, rest = keys.split(".", 1) 
     if key not in d: 
      d[key] = {} 
     put(d[key], rest, item) 
    else: 
     d[keys] = item 

def get(d, keys): 
    if "." in keys: 
     key, rest = keys.split(".", 1) 
     return get(d[key], rest) 
    else: 
     return d[keys] 
+1

Esto podría mejorarse llamando a split solo una vez y pasando la lista resultante a la función recursiva. – l4mpi

+0

Este * casi * lo tiene. El problema es que put no tiene en cuenta si existe una clave, pero es un valor de texto que debe sobrescribirse con un dict. – hikaru

+1

Lo usé con modificaciones. Muy similar a la respuesta de @ l4mpi. – hikaru

2

¿Qué tal un enfoque iterativo?

def create_keys(d, keys): 
    for k in keys.split("."): 
     if not k in d: d[k] = {} #if the key isn't there yet add it to d 
     d = d[k]     #go one level down and repeat 

Si necesita el último valor clave para asignar a cualquier otra cosa que un diccionario que podría pasar el valor como un argumento adicional y establecer este después del bucle:

def create_keys(d, keys, value): 
    keys = keys.split(".") 
    for k in keys[:-1]: 
     if not k in d: d[k] = {} 
     d = d[k]    
    d[keys[-1]] = value 
18

Se podría utilizar un infinito , anidado defaultdict:

>>> from collections import defaultdict 
>>> infinitedict = lambda: defaultdict(infinitedict) 
>>> d = infinitedict() 
>>> d['key1']['key2']['key3']['key4']['key5'] = 'test' 
>>> d['key1']['key2']['key3']['key4']['key5'] 
'test' 

Dada su cadena de puntos, esto es lo que puede hacer:

>>> import operator 
>>> keys = "a.b.c".split(".") 
>>> lastplace = reduce(operator.getitem, keys[:-1], d) 
>>> lastplace.has_key(keys[-1]) 
False 

Se puede establecer un valor:

>>> lastplace[keys[-1]] = "something" 
>>> reduce(operator.getitem, keys, d) 
'something' 
>>> d['a']['b']['c'] 
'something' 
+6

Eso es profundo. 'infinitedict = lambda: defaultdict (infinitedict) ' +1 – dokkaebi

0
d = {"a":{}} 
k = "a.b.c".split(".") 

def f(d, i): 
    if i >= len(k): 
     return "winning!" 
    c = k[i] 
    d[c] = f(d.get(c, {}), i + 1) 
    return d 

print f(d, 0) 
"{'a': {'b': {'c': 'winning!'}}}" 
Cuestiones relacionadas