Existe-t-il un moyen de faire de defaultdict la valeur par défaut de defaultdict? (c'est-à-dire defaultdict récursif de niveau infini?)
Je veux pouvoir faire:
x = defaultdict(...stuff...)
x[0][1][0]
{}
Donc, je peux faire x = defaultdict(defaultdict)
, mais ce n'est qu'un deuxième niveau:
x[0]
{}
x[0][0]
KeyError: 0
Il y a des recettes qui peuvent faire ceci. Mais peut-on le faire simplement en utilisant les arguments normaux defaultdict?
Notez que ceci demande comment faire un defaultdict récursif de niveau infini, donc distinct de Python: defaultdict de defaultdict?, qui était comment faire un defaultdict à deux niveaux.
Je vais probablement finir par utiliser le motif , mais lorsque j'ai réalisé que je ne savais pas comment faire cela, cela m'a intéressé.
Pour un nombre arbitraire de niveaux:
def rec_dd():
return defaultdict(rec_dd)
>>> x = rec_dd()
>>> x['a']['b']['c']['d']
defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
>>> print json.dumps(x)
{"a": {"b": {"c": {"d": {}}}}}
Bien sûr, vous pouvez aussi faire cela avec un lambda, mais je trouve que les lambdas sont moins lisibles. Dans tous les cas, cela ressemblerait à ceci:
rec_dd = lambda: defaultdict(rec_dd)
Les autres réponses ici vous expliquent comment créer un defaultdict
qui contient "infiniment beaucoup" defaultdict
, mais elles ne répondent pas à votre besoin initial, qui était simplement d'avoir deux -depth defaultdict.
Vous avez peut-être été à la recherche de:
defaultdict(lambda: defaultdict(dict))
Les raisons pour lesquelles vous pourriez préférer cette construction sont:
defaultdict
d'être autre chose qu'un dictionnaire, par exemple,: defaultdict(lambda: defaultdict(list))
ou defaultdict(lambda: defaultdict(set))
Il y a un truc astucieux pour faire ça:
tree = lambda: defaultdict(tree)
Ensuite, vous pouvez créer votre x
avec x = tree()
.
Semblable à la solution de BrenBarn, mais ne contenant pas le nom de la variable tree
, il fonctionne donc même après des modifications du dictionnaire de variables:
tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a))))
Ensuite, vous pouvez créer chaque nouveau x
avec x = tree()
.
Pour la version def
, nous pouvons utiliser l'étendue de fermeture de fonction pour protéger la structure de données de la faille dans laquelle les instances existantes cessent de fonctionner si le nom tree
est un rebond. Cela ressemble à ceci:
from collections import defaultdict
def tree():
def the_tree():
return defaultdict(the_tree)
return the_tree()
Je proposerais également une implémentation plus stylée en POO, qui supporte l'imbrication infinie ainsi que le formatage correct repr
.
class NestedDefaultDict(defaultdict):
def __init__(self, *args, **kwargs):
super(NestedDefaultDict, self).__init__(NestedDefaultDict, *args, **kwargs)
def __repr__(self):
return repr(dict(self))
Usage:
my_dict = NestedDefaultDict()
my_dict['a']['b'] = 1
my_dict['a']['c']['d'] = 2
my_dict['b']
print(my_dict) # {'a': {'b': 1, 'c': {'d': 2}}, 'b': {}}