Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Dict : Ajouter de manière récursive des clés

+3 votes

Je cherche à pouvoir faire un dictionnaire custom de sorte à donner l’instruction ci-dessous sans avoir un KeyError :

t = MyDict()
t[0][1][2] = 3

Je sais que c'est déjà possible grâce à collections mais je me demandais, d'un point de vue purement éducatif si ça pouvait aussi se faire "facilement" en utilisant un objet custom qui hériterait de dict. J'ai tenté quelque chose mais j'ai l'impression que je suis à coté de la plaque ce matin.

class RecDic(dict) :
    def __setitem__(self, key, value) :
        print "setter", key, value, self
        super(RecDic, self).__setitem__(key, value) 

    def __getitem__(self, key) :
        print "getter", key, self
        try : return super(RecDic, self).__getitem__(key) 
        except KeyError : 
            print "key error", self, key
            self.__setitem__(key, {})
            print "set new key", self, key
            return self.__getitem__(key) 

t = RecDic()
t[0][1] = 2
print t
t = RecDic()
t[0][1][2] = 3
print t

Ce qui me donne :

getter 0 {}
key error {} 0
setter 0 {} {}
set new key {0: {}} 0
getter 0 {0: {}}
{0: {1: 2}}
getter 0 {}
key error {} 0
setter 0 {} {}
set new key {0: {}} 0
getter 0 {0: {}}
Traceback (most recent call last):
  File "essai.py", line 19, in <module>
    t[0][1][2] = 3
KeyError: 1
demandé 27-Jul-2015 par Jev (388 points)

2 Réponses

+4 votes
 
Meilleure réponse

La feature que tu souhaites réaliser s'appelle auto-vivification (coucou perl). Une bonne façon de le faire simplement en surchargeant dict est :

class AutoDict(dict):
    def __missing__(self, key):
        "autovivification feature"
        dct = self[key] = AutoDict()
        return dct
répondu 30-Jul-2015 par yoch (2,510 points)
sélectionné 31-Jul-2015 par Jev

Pas mal ! Je ne connaissais pas du tout le terme. Merci pour l'information :)

joli, je ne connaissais pas __missing__.

+5 votes

La valeur que tu set sur KeyError un est un dictionnaire builtin, donc avec le comportement par defaut.
Il faut que tu assigne une instance de ta classe RecDic (cf le code en commentaire)

def __getitem__(self, key) :
    print "getter", key, self
    try : return super(RecDic, self).__getitem__(key) 
    except KeyError : 
        print "key error", self, key
        self.__setitem__(key, {}) # self.__setitem__(key, RecDic()) 
        print "set new key", self, key
        return self.__getitem__(key) 
répondu 27-Jul-2015 par jc (2,674 points)

Effectivement, j'étais pas assez réveillé ce matin je crois. Merci beaucoup en tous cas !

L'implémentation de la même fonctionnalité dans batbelt de sametmax peu t'intéresser : https://github.com/sametmax/Bat-belt/blob/master/batbelt/structs.py#L81
Ça n'impose pas de type d'objet particulier donc fonctionne dans tous les cas.

Edit: au temps pour moi, ce n'est pas la même chose : la lib n’implémentent que l’accès a une valeur, et renvoi un valeur défaut. Pas d'affectation.

...