web-dev-qa-db-fra.com

Existe-t-il un moyen intelligent de passer la clé à default_factory de defaultdict?

Une classe a un constructeur qui prend un paramètre:

class C(object):
    def __init__(self, v):
        self.v = v
        ...

Quelque part dans le code, il est utile que les valeurs d'un dict connaissent leurs clés.
Je veux utiliser un dict par défaut avec la clé passée aux valeurs par défaut du nouveau-né:

d = defaultdict(lambda : C(here_i_wish_the_key_to_be))

Aucune suggestion?

84

Il est à peine qualifié de intelligent - mais le sous-classement est votre ami:

class keydefaultdict(defaultdict):
    def __missing__(self, key):
        if self.default_factory is None:
            raise KeyError( key )
        else:
            ret = self[key] = self.default_factory(key)
            return ret

d = keydefaultdict(C)
d[x] # returns C(x)
117
Jochen Ritzel

Non, il n'y en a pas.

L'implémentation defaultdict ne peut pas être configurée pour transmettre le key manquant au default_factory hors de la boîte. Votre seule option est d'implémenter votre propre sous-classe defaultdict, comme suggéré par @JochenRitzel, ci-dessus.

Mais ce n'est pas "intelligent" ou presque aussi propre qu'une solution de bibliothèque standard (si elle existait). Ainsi, la réponse à votre question succincte, oui/non est clairement "Non".

Il est dommage que la bibliothèque standard manque un outil si fréquemment utilisé.

25
Stuart Berg

Je ne pense pas que vous ayez besoin de defaultdict ici. Pourquoi ne pas simplement utiliser dict.setdefault méthode?

>>> d = {}
>>> d.setdefault('p', C('p')).v
'p'

Cela créera bien sûr de nombreuses instances de C. En cas de problème, je pense que l'approche plus simple fera l'affaire:

>>> d = {}
>>> if 'e' not in d: d['e'] = C('e')

Ce serait plus rapide que le defaultdict ou toute autre alternative pour autant que je puisse voir.

ETA concernant la vitesse de in test par rapport à la clause try-except:

>>> def g():
    d = {}
    if 'a' in d:
        return d['a']


>>> timeit.timeit(g)
0.19638929363557622
>>> def f():
    d = {}
    try:
        return d['a']
    except KeyError:
        return


>>> timeit.timeit(f)
0.6167065411074759
>>> def k():
    d = {'a': 2}
    if 'a' in d:
        return d['a']


>>> timeit.timeit(k)
0.30074866358404506
>>> def p():
    d = {'a': 2}
    try:
        return d['a']
    except KeyError:
        return


>>> timeit.timeit(p)
0.28588609450770264
6
SilentGhost