2012-04-18 9 views
8

Siento que vi una forma de hacer esto recientemente. Digamos que tengo un dict vacío y quiero establecer un valor en un dict anidado dentro de ese dict vacío, pero obviamente esa dict anidada aún no se ha creado. ¿Hay una forma de 1 línea para crear las teclas intermedias? Esto es lo que quiero hacer:Establecer valor dict anidado y crear claves intermedias

mydict = {} 
mydict['foo']['bar']['foobar'] = 25 

Si se ejecuta el código que obtendrá una excepción para KeyError 'foo'. ¿Hay una función para crear claves intermedias?

Gracias.

+0

posible duplicado de [Generar claves dict de Python sobre la marcha] (http://stackoverflow.com/questions/3405073/generating-python-dict-keys-on-the -fly) – unutbu

Respuesta

14
from collections import defaultdict 
recursivedict = lambda: defaultdict(recursivedict) 
mydict = recursivedict() 

Al acceder a mydict['foo'], establece mydict['foo'] a otro recursivedict. De hecho, construirá un recursivedict para mydict['foo']['bar']['foobar'], pero luego se descartará al asignarlo al 25.

+0

Esto es exactamente lo que había recordado leyendo sobre-- gracias. – skandocious

+0

Esto podría romper en este 'mydict ['foo'] = 15⏎ mydict ['foo'] ['bar'] ['foobar'] = 25'. En una gran base de código, el OP no puede recordar qué valor le asignó anteriormente. – nehemiah

+0

@itsneo Eso es verdad. No creo que haya una buena forma de evitar eso; tendrías que hacer envoltorios de retorno '__getitem__' alrededor del elemento almacenado anulando el' '__getitem__' de ese elemento, lo que dificultaría poner una lista o lo que sea en el diccionario. – Dougal

0

No sé por qué te gustaría, pero:

>>> from collections import defaultdict as dd 
>>> mydict = dd(lambda: dd(lambda: {})) 
>>> mydict['foo']['bar']['foobar'] = 25 
>>> mydict 
defaultdict(<function <lambda> at 0x021B8978>, {'foo': defaultdict(<function <lambda> at 0x021B8618>, {'bar': {'foobar': 25}})}) 
+0

Esto solo le permite anidar hasta tres capas; desea que la función sea recursiva, como en [mi respuesta] (http://stackoverflow.com/a/10218517/344821), para poder continuar. – Dougal

+0

@Dougal: su respuesta no decía eso cuando escribí esto. – MattH

+0

@Dougal: si tres niveles profundos son todo lo que necesita OP, es suficiente. –

3

Una opción alternativa - en función de sus usos, es el uso de tuplas como claves en lugar de los diccionarios anidados:

mydict = {} 
mydict['foo', 'bar', 'foobar'] = 25 

Este funcionará perfectamente bien a menos que desee obtener una rama del árbol en cualquier punto (en este caso no puede obtener mydict ['foo']).

Si sabía cuántas capas de anidación desea, también podría usar functools.partial en lugar de lambda.

from functools import partial 
from collections import defaultdict 

tripledict = partial(defaultdict, partial(defaultdict, dict)) 
mydict = tripledict() 
mydict['foo']['bar']['foobar'] = 25 

cual algunas personas encuentran más fácil de leer, y es más rápido para crear instancias de que la solución basada en lambda equivalentes:

python -m timeit -s "from functools import partial" -s "from collections import defaultdict" -s "tripledefaultdict = partial(defaultdict, partial(defaultdict, dict))" "tripledefaultdict()" 
1000000 loops, best of 3: 0.281 usec per loop 

python -m timeit -s "from collections import defaultdict" -s "recursivedict = lambda: defaultdict(recursivedict)" "recursivedict()" 
1000000 loops, best of 3: 0.446 usec per loop 

Aunque, como siempre, no hay ningún punto de optimización hasta que sabe que hay es un cuello de botella, así que escoja lo que es más útil y legible antes de lo que es más rápido.

+0

Cosas interesantes. Nunca pensé usar una tupla como una clave dict. Aunque esto no ayuda en mi caso, es ciertamente útil. ¡Gracias! – skandocious

+0

Por supuesto, es posible obtener ramas de árbol con la solución basada en tuplas a través de algo como 'prefix = ('foo', 'bar'); l = len (prefijo); branch = {k [l:]: v para k, v en mydict.items() si k [: l] == prefijo} '- aunque eso necesita iterar sobre todas las claves. – Dougal

+0

@Dougal Sí, en ese punto, es mejor que simplemente uses los '' dict''s anidados. –

Cuestiones relacionadas