Al revisar un diccionario nested usando una cadena de notación de puntos “abcde”, crea automáticamente los niveles faltantes

Este me está perdiendo la cabeza. Dado el siguiente diccionario:

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

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

  k = "abc" 

Necesito determinar si el diccionario tiene la clave ‘c’, así que puedo agregarlo si no lo tiene.

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

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

pero no puedo averiguar cómo “reducir” una comprobación has_key o algo así.

Mi problema final es el siguiente: dado “abcde”, necesito crear todos los elementos necesarios en el diccionario, pero no aplastarlos si ya existen. Si alguien sabe una manera increíble de hacer todo eso, serás mi héroe.

… o usando 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] 

Podrías usar un valor por defecto infinito y nested:

 >>> 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 tu cadena de puntos, esto es lo que puedes hacer:

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

Puede establecer un valor:

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

¿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 de clave para asignarlo a cualquier otra cosa que no sea un diccionario, podría pasar el valor como un argumento adicional y establecerlo 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 
 d = {"a":{}} k = "abc".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!'}}}"