2010-08-04 12 views
8

Trabajar con predice pitón profundamente anidados, me gustaría ser capaz de asignar valores en una estructura de datos como esto:claves de diccionario Generación sobre la marcha

mydict[key][subkey][subkey2]="value" 

sin tener que comprobar que mydict [clave], etc. .de hecho están configurados para ser un dict, por ejemplo utilizando

if not key in mydict: mydict[key]={} 

La creación de subdiccionarios debe realizarse sobre la marcha. ¿Cuál es la manera más elegante de permitir algo equivalente, tal vez usando decoradores en el estándar <type 'dict'>?

+0

Pregunta relacionada: http://stackoverflow.com/questions/3122566/in-a-python-dict-of-dicts-how-do-you-emulate-perls-auto-vivification-behavior/3122575#3122575 – unutbu

Respuesta

19
class D(dict): 
    def __missing__(self, key): 
     self[key] = D() 
     return self[key] 

d = D() 
d['a']['b']['c'] = 3 
+1

Tienes que tener cuidado con esto. 'd ['a'] = 2 d ['a'] ['b'] = 2' fallará. – unholysampler

+0

Sí, pero eso está implícito en mi pregunta: estoy pidiendo un tipo mixto de dictados y valores. – relet

+0

Para resolver el caso d ['a'] = 2 d ['a'] ['b'] = 2, incluso escribiría la clase D (dict): def __missing __ (self, key): value = self [tecla] = tipo de (auto)() valor de retorno def __getitem __ (self, clave): valores = dict .__ GetItem __ (self, clave) si isinstance (valores, dict): valores = SafeDict (valores) si isinstance (valores, lista): para i, v en enumerate (valores): si isinstance (v, dict): valores [i] = SafeDict (v) valores de retorno – user2346922

9

se puede utilizar una tupla como la clave para el dict y luego usted no tiene que preocuparse en absoluto subdictionaries:

mydict[(key,subkey,subkey2)] = "value" 

Alternativamente, si usted realmente necesita tener subdictionaries por alguna razón usted podría use collections.defaultdict.

Durante dos niveles Esto es sencillo:

>>> from collections import defaultdict 
>>> d = defaultdict(dict) 
>>> d['key']['subkey'] = 'value' 
>>> d['key']['subkey'] 
'value' 

Para tres es un poco más complejo:

>>> d = defaultdict(lambda: defaultdict(dict)) 
>>> d['key']['subkey']['subkey2'] = 'value' 
>>> d['key']['subkey']['subkey2'] 
'value' 

Cuatro y más niveles se dejan como ejercicio para el lector. :-)

+0

Asombroso. Me alegro de haber preguntado, aunque solo sea porque no entiendo cómo podría pasar esto por alto. :) – relet

+0

Buena respuesta. ¿Puedes proporcionar cómo hacerlo con un 'defaultdict'? Anidar una vez es fácil: 'mydict = defaultdict (dict)'. Pero, ¿hay una solución elegante para anidar dos veces? –

+0

@jellybean - acaba de agregar la solución de tres niveles; no es tan malo. –

2

Me gusta más la respuesta de Dave, pero aquí hay una alternativa.

from collections import defaultdict 
d = defaultdict(lambda : defaultdict(int)) 
>>> d['a']['b'] += 1 
>>> d 
defaultdict(<function <lambda> at 0x652f0>, {'a': defaultdict(<type 'int'>, {'b': 1})}) 
>>> d['a']['b'] 
1 

http://tumble.philadams.net/post/85269428/python-nested-defaultdicts

Es, definitivamente, no es bonito tener que utilizar a lambdas implementa las colecciones en default interiores, pero al parecer es necesario.

+2

Las lambdas nunca son necesarias: siempre puede usar una función nombrada. En este caso, usar una función nombrada significaría al menos que 'repr' tiene algo un poco más significativo que' lambda'. – Duncan

Cuestiones relacionadas