Valor predeterminado nested de defaultdict

¿Hay alguna manera de hacer que un valor predeterminado también sea el predeterminado para el valor predeterminado? (es decir, defaultdict recursivo de nivel infinito?)

Quiero poder hacer:

x = defaultdict(...stuff...) x[0][1][0] {} 

Entonces, puedo hacer x = defaultdict(defaultdict) , pero eso es solo un segundo nivel:

 x[0] {} x[0][0] KeyError: 0 

Hay recetas que pueden hacer esto. ¿Pero se puede hacer simplemente usando los argumentos predeterminados normales?

Tenga en cuenta que esto es preguntar cómo hacer un valor predeterminado recursivo de nivel infinito, por lo que es distinto de Python: valor predeterminado de valor predeterminado? , que era cómo hacer un defaultdict de dos niveles.

Probablemente termine usando el patrón de manojo , pero cuando me di cuenta de que no sabía cómo hacerlo, me interesé.

Para un número arbitrario de niveles:

 def rec_dd(): return defaultdict(rec_dd) >>> x = rec_dd() >>> x['a']['b']['c']['d'] defaultdict(, {}) >>> print json.dumps(x) {"a": {"b": {"c": {"d": {}}}}} 

Por supuesto, también puede hacer esto con un lambda, pero me parece que las lambdas son menos legibles. En cualquier caso se vería así:

 rec_dd = lambda: defaultdict(rec_dd) 

Las otras respuestas aquí le dicen cómo crear una defaultdict que contiene “infinitas muchas”, pero no abordan lo que creo que pudo haber sido su necesidad inicial, que era simplemente tener una sentencia predeterminada de dos profundidades.

Es posible que haya estado buscando:

 defaultdict(lambda: defaultdict(dict)) 

Las razones por las que podrías preferir este constructo son:

  • Es más explícito que la solución recursiva y, por lo tanto, probablemente más comprensible para el lector.
  • Esto permite que la “hoja” del defaultdict sea ​​algo más que un diccionario, por ejemplo,: defaultdict(lambda: defaultdict(list)) o defaultdict(lambda: defaultdict(set))

Hay un truco ingenioso para hacer eso:

 tree = lambda: defaultdict(tree) 

Luego puedes crear tu x con x = tree() .

Similar a la solución de BrenBarn, pero no contiene el nombre del tree variables dos veces, por lo que funciona incluso después de los cambios en el diccionario de variables:

 tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a)))) 

Luego puedes crear cada nueva x con x = tree() .


Para la versión def , podemos usar el scope de cierre de función para proteger la estructura de datos de la falla donde las instancias existentes dejan de funcionar si el nombre del tree rebota. Se parece a esto:

 from collections import defaultdict def tree(): def the_tree(): return defaultdict(the_tree) return the_tree()