191 votes

Plusieurs niveaux de la collection.defaultdict' en Python

Grâce à des gens, j'ai découvert les possibilités offertes par collections.defaultdict, notamment dans la lisibilité et de la vitesse. J'ai mis à utiliser avec succès.

Maintenant, je tiens à mettre en œuvre trois niveaux de dictionnaires, les deux premiers étant defaultdict et le plus bas d'un cours d' int. Je ne trouve pas le moyen le plus approprié pour ce faire. Voici ma tentative:

from collections import defaultdict
d = defaultdict(defaultdict)
a = [("key1", {"a1":22, "a2":33}),
     ("key2", {"a1":32, "a2":55}),
     ("key3", {"a1":43, "a2":44})]
for i in a:
    d[i[0]] = i[1]

Maintenant cela fonctionne, mais le suivant, qui est le comportement souhaité, n'est-ce pas:

d["key4"]["a1"] + 1

Je soupçonne que je devrais avoir déclaré quelque part que le deuxième niveau de l' defaultdict est de type int, mais je n'ai pas trouvé où et comment le faire.

La raison pour laquelle je suis à l'aide de defaultdict , en premier lieu, pour éviter d'avoir à initialiser le dictionnaire pour chaque nouvelle clé.

Plus élégant suggestion?

Grâce pythoneers!

371voto

interjay Points 51000

Utilisation:

d = defaultdict(lambda: defaultdict(int))

Cela va créer un nouveau defaultdict(int) chaque fois qu'une nouvelle clé est accessible en d.

14voto

miles82 Points 1810

Regardez nosklo de réponse ici pour plus de solution générale.

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

Test:

a = AutoVivification()

a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6

print a

Sortie:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}

7voto

spazm Points 1545

Comme par @rschwieb demande d' D['key'] += 1, nous pouvons étendre sur la précédente en remplaçant outre, en définissant __add__ méthode, pour faire de cette se comportent plus comme un collections.Counter()

Premier __missing__ seront appelés à créer une nouvelle valeur vide, qui sera passé en __add__. Nous testons la valeur, en comptant sur des valeurs vides pour être False.

Voir l'émulation de types numériques pour plus d'informations sur le remplacement.

from numbers import Number


class autovivify(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

    def __add__(self, x):
        """ override addition for numeric types when self is empty """
        if not self and isinstance(x, Number):
            return x
        raise ValueError

    def __sub__(self, x):
        if not self and isinstance(x, Number):
            return -1 * x
        raise ValueError

Exemples:

>>> import autovivify
>>> a = autovivify.autovivify()
>>> a
{}
>>> a[2]
{}
>>> a
{2: {}}
>>> a[4] += 1
>>> a[5][3][2] -= 1
>>> a
{2: {}, 4: 1, 5: {3: {2: -1}}}

Plutôt que de vérifier argument est un Nombre (très non-python, amirite!) nous pourrions nous contenter de fournir une valeur par défaut de 0, puis à tenter l'opération:

class av2(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

    def __add__(self, x):
        """ override addition when self is empty """
        if not self:
            return 0 + x
        raise ValueError

    def __sub__(self, x):
        """ override subtraction when self is empty """
        if not self:
            return 0 - x
        raise ValueError

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X