240 votes

Python: Comment "parfaitement" remplacer un dict

Comment puis-je faire comme "parfait" une sous-classe de dict que possible? L'objectif final est de pour avoir un simple dict dans lequel les touches sont minuscules.

Il semblerait que devrait être un petit ensemble de primitives que je peux remplacer à faire ce travail, mais toutes mes recherches et de tentatives ont fait croire que ce n'est pas le cas:

  • Si je remplacer __getitem__/__setitem__, alors get/set ne fonctionnent pas. Comment puis-je le faire fonctionner? Sûrement, je n'ai pas besoin de les mettre en œuvre individuellement?

  • Suis-je prévenir le décapage de travail, et j'en ai besoin pour mettre en oeuvre __setstate__ etc?

  • Dois-je besoin d' repr, update et __init__?

  • Devrais-je utiliser mutablemapping (il me semble que l'on ne devrait pas utiliser UserDict ou DictMixin)? Si oui, comment? Les docs ne sont pas exactement en l'éclairant.

Voici mon premier aller à elle, get() ne fonctionne pas, au moins, et sans doute il y a beaucoup de petits problèmes subtils:

class arbitrary_dict(dict):
    """A dictionary that applies an arbitrary key-altering function
       before accessing the keys."""

    def __keytransform__(self, key):
        return key

    # Overridden methods. List from 
    # http://stackoverflow.com/questions/2390827/how-to-properly-subclass-dict

    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

    # Note: I'm using dict directly, since super(dict, self) doesn't work.
    # I'm not sure why, perhaps dict is not a new-style class.

    def __getitem__(self, key):
        return dict.__getitem__(self, self.__keytransform__(key))

    def __setitem__(self, key, value):
        return dict.__setitem__(self, self.__keytransform__(key), value)

    def __delitem__(self, key):
        return dict.__delitem__(self, self.__keytransform__(key))

    def __contains__(self, key):
        return dict.__contains__(self, self.__keytransform__(key))


class lcdict(arbitrary_dict):
    def __keytransform__(self, key):
        return str(key).lower()

261voto

Jochen Ritzel Points 42916

Vous pouvez écrire un objet qui se comporte comme un dict assez facilement avec ABCs (Classes de Base abstraites) à partir des collections du module. Il vous indique même si vous avez manqué une méthode, donc, ci-dessous est la version minimale qui ferme l'ABC.

import collections


class TransformedDict(collections.MutableMapping):
    """A dictionary that applies an arbitrary key-altering
       function before accessing the keys"""

    def __init__(self, *args, **kwargs):
        self.store = dict()
        self.update(dict(*args, **kwargs))  # use the free update to set keys

    def __getitem__(self, key):
        return self.store[self.__keytransform__(key)]

    def __setitem__(self, key, value):
        self.store[self.__keytransform__(key)] = value

    def __delitem__(self, key):
        del self.store[self.__keytransform__(key)]

    def __iter__(self):
        return iter(self.store)

    def __len__(self):
        return len(self.store)

    def __keytransform__(self, key):
        return key

Vous obtenez quelques méthodes libres de l'ABC:

class MyTransformedDict(TransformedDict):

    def __keytransform__(self, key):
        return key.lower()


s = MyTransformedDict([('Test', 'test')])

assert s.get('TEST') is s['test']   # free get
assert 'TeSt' in s                  # free __contains__
                                    # free setdefault, __eq__, and so on

import pickle
assert pickle.loads(pickle.dumps(s)) == s
                                    # works too since we just use a normal dict

Je ne voudrais pas sous-classe dict (ou d'autres objets internes) directement. Souvent, il n'a pas de sens, parce que ce que vous voulez faire est de mettre en œuvre l'interface d'un dict. Et c'est exactement ce que Abc.

1voto

Duncan Points 25356

Autant que je sache, mis à part la confusion sur super , votre seul problème est que vous n'avez pas suivi la liste des méthodes que votre commentaire dit suivre.

Ajoutez le remplacement manquant pour update et il semble que cela fonctionne. Sans la substitution à update les clés transmises à l'initialiseur ne sont pas transformées.

 class arbitrary_dict(dict):
    """A dictionary that applies an arbitrary key-altering function
       before accessing the keys"""

    def __keytransform__(self, key):
        return key

    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

    def __getitem__(self, key):
        return super(arbitrary_dict,
                     self).__getitem__(self.__keytransform__(key))

    def __setitem__(self, key, value):
        return super(arbitrary_dict,
                     self).__setitem__(self.__keytransform__(key), value)

    def __delitem__(self, key):
        return super(arbitrary_dict,
                     self).__delitem__(self.__keytransform__(key))

    def __contains__(self, key):
        return super(arbitrary_dict,
                     self).__contains__(self.__keytransform__(key))

    def update(self, *args, **kwargs):
        trans = self.__keytransform__
        super(arbitrary_dict, self).update(
            *[(trans(k), v) for k, v in args],
            **dict((trans(k), kwargs[k]) for k in kwargs))
 

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