15 votes

Accès au dictateur python en utilisant une chaîne de recherche de clé imbriquée

Je cherche à créer un mécanisme simple de "lookup" imbriqué dans python, et je voulais m'assurer qu'il n'y avait pas déjà quelque chose de caché dans les vastes bibliothèques de python qui ne fait pas déjà cela avant de le créer.

Je cherche à prendre un dict qui est formaté comme ceci

my_dict = { 
  "root": { 
    "secondary": { 
      "user1": { 
          "name": "jim", 
          "age": 24 
      }, 
      "user2": { 
        "name": "fred", 
        "age": 25 
      } 
    } 
  } 
}

et j'essaie d'avoir un moyen d'accéder aux données en utilisant une notation décimale qui serait quelque chose de semblable à

root.secondary.user2

et renvoie la dictée résultante comme réponse. Je pense qu'il doit y avoir quelque chose qui fait cela et je pourrais en écrire un sans trop de difficulté mais je veux être sûr que je ne recrée pas quelque chose qui pourrait manquer dans la documentation. Merci

30voto

Sven Marnach Points 133943

Il n'y a rien dans la bibliothèque standard à cet effet, mais il est assez facile de le coder soi-même :

>>> key = "root.secondary.user2"
>>> reduce(dict.get, key.split("."), my_dict)
{'age': 25, 'name': 'fred'}

Cela exploite le fait que la recherche de la clé k dans le dictionnaire d peut s'écrire comme suit dict.get(d, k) . En appliquant ceci de manière itérative en utilisant reduce() conduit au résultat souhaité.

Editar : Pour être complet, trois fonctions pour obtenir, définir ou supprimer des clés de dictionnaire en utilisant cette méthode :

def get_key(my_dict, key):
    return reduce(dict.get, key.split("."), my_dict)

def set_key(my_dict, key, value):
    key = key.split(".")
    my_dict = reduce(dict.get, key[:-1], my_dict)
    my_dict[key[-1]] = value

def del_key(my_dict, key):
    key = key.split(".")
    my_dict = reduce(dict.get, key[:-1], my_dict)
    del my_dict[key[-1]]

2voto

Frg Points 406

Tu peux avoir ça. Vous pouvez sous-classer dict, ajouter la recherche de clé (et même conserver le nom dict) en utilisant un code similaire à celui ci-dessous. Le site {...} utilisera cependant toujours la classe dict intégrée (désormais appelée orig_dict), et vous devrez donc l'entourer, comme suit : Dict({...}) . Cette implémentation convertit récursivement les dictionnaires vers la nouvelle forme, de sorte que vous n'avez pas à utiliser la méthode ci-dessus pour les entrées de dictionnaire qui sont elles-mêmes des dictionnaires simples.

orig_dict = dict
class Dict(orig_dict):
    def __init__(self, *args, **kwargs):
        super(Dict, self).__init__(*args, **kwargs)
        for k, v in self.iteritems():
            if type(v) == orig_dict and not isinstance(v, Dict):
                super(Dict, self).__setitem__(k, Dict(v))
    def __getattribute__(self, k):
        try: return super(Dict, self).__getattribute__(k)
        except: return self.__getitem__(k)
    def __setattr__(self, k, v):
        if self.has_key(k): self.__setitem__(k, v)
        else: return super(Dict, self).__setattr__(k, v)
    def __delattr__(self, k):
        try: self.__delitem__(k)
        except: super(Dict, self).__delattr__(k)
    def __setitem__(self, k, v):
        toconvert = type(v) == orig_dict and not isinstance(v, Dict)
        super(Dict, self).__setitem__(k, Dict(v) if toconvert else v)

# dict = Dict  <-- you can even do this but I advise against it

# testing:
b = Dict(a=1, b=Dict(c=2, d=3))
c = Dict({'a': 1, 'b': {'c': 2, 'd': 3}})
d = Dict(a=1, b={'c': 2, 'd': {'e': 3, 'f': {'g': 4}}})

b.a = b.b
b.b = 1
d.b.d.f.g = 40
del d.b.d.e
d.b.c += d.b.d.f.g
c.b.c += c.a
del c.a
print b
print c
print d

1voto

S.Lott Points 207588

La récursion fonctionne toujours.

def walk_into( dict, key ):
    head, _, tail = key.partition('.')
    if tail:
        return walk_into( dict[head], tail )
    return dict, key
d, k = walk_into( my_dict, "root.secondary.user2" )

d[k] peut être utilisé pour obtenir ou mettre une nouvelle valeur.

0voto

Saska Karsi Points 11

J'ai une implémentation assez complète pour cela et d'autres choses. aquí . Référentiel aquí , trict.util combiné avec le __get__ méthode dans trict.trict pourrait avoir le matériel dont vous avez besoin si vous n'avez pas envie de l'installer. De plus, il est actuellement dans conda-forge même si le README peut dire le contraire si je ne l'ai pas mis à jour avant que vous lisiez ceci.

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