183 votes

Accéder à des éléments de dictionnaire imbriqués via une liste de clés ?

J'ai une structure de dictionnaire complexe à laquelle je voudrais accéder via une liste de clés pour adresser l'élément correct.

dataDict = {
    "a":{
        "r": 1,
        "s": 2,
        "t": 3
        },
    "b":{
        "u": 1,
        "v": {
            "x": 1,
            "y": 2,
            "z": 3
        },
        "w": 3
        }
}    

maplist = ["a", "r"]

ou

maplist = ["b", "v", "y"]

J'ai créé le code suivant qui fonctionne mais je suis sûr qu'il y a un moyen meilleur et plus efficace de le faire si quelqu'un a une idée.

# Get a given data from a dictionary with position provided as a list
def getFromDict(dataDict, mapList):    
    for k in mapList: dataDict = dataDict[k]
    return dataDict

# Set a given data in a dictionary with position provided as a list
def setInDict(dataDict, mapList, value): 
    for k in mapList[:-1]: dataDict = dataDict[k]
    dataDict[mapList[-1]] = value

2voto

xyres Points 552

Et si vous utilisiez des fonctions récursives ?

Pour obtenir une valeur :

def getFromDict(dataDict, maplist):
    first, rest = maplist[0], maplist[1:]

    if rest: 
        # if `rest` is not empty, run the function recursively
        return getFromDict(dataDict[first], rest)
    else:
        return dataDict[first]

Et pour fixer une valeur :

def setInDict(dataDict, maplist, value):
    first, rest = maplist[0], maplist[1:]

    if rest:
        try:
            if not isinstance(dataDict[first], dict):
                # if the key is not a dict, then make it a dict
                dataDict[first] = {}
        except KeyError:
            # if key doesn't exist, create one
            dataDict[first] = {}

        setInDict(dataDict[first], rest, value)
    else:
        dataDict[first] = value

2voto

Poh Zi How Points 310

J'ai résolu ça avec la récursion :

def get(d,l):
    if len(l)==1: return d[l[0]]
    return get(d[l[0]],l[1:])

En utilisant votre exemple :

dataDict = {
    "a":{
        "r": 1,
        "s": 2,
        "t": 3
        },
    "b":{
        "u": 1,
        "v": {
            "x": 1,
            "y": 2,
            "z": 3
        },
        "w": 3
        }
}
maplist1 = ["a", "r"]
maplist2 = ["b", "v", "y"]
print(get(dataDict, maplist1)) # 1
print(get(dataDict, maplist2)) # 2

1voto

Arount Points 4988

Un pur style Python, sans aucune importation :

def nested_set(element, value, *keys):
    if type(element) is not dict:
        raise AttributeError('nested_set() expects dict as first argument.')
    if len(keys) < 2:
        raise AttributeError('nested_set() expects at least three arguments, not enough given.')

    _keys = keys[:-1]
    _element = element
    for key in _keys:
        _element = _element[key]
    _element[keys[-1]] = value

example = {"foo": { "bar": { "baz": "ok" } } }
keys = ['foo', 'bar']
nested_set(example, "yay", *keys)
print(example)

Sortie

{'foo': {'bar': 'yay'}}

1voto

Pulkit Points 239

Une autre façon de faire si vous ne voulez pas lever des erreurs si l'une des clés est absente (afin que votre code principal puisse s'exécuter sans interruption) :

def get_value(self,your_dict,*keys):
    curr_dict_ = your_dict
    for k in keys:
        v = curr_dict.get(k,None)
        if v is None:
            break
        if isinstance(v,dict):
            curr_dict = v
    return v

Dans ce cas, si l'une des touches de saisie n'est pas présente, None est renvoyé, ce qui peut être utilisé comme un contrôle dans votre code principal pour effectuer une tâche alternative.

1voto

nehemiahjacob Points 122

Il est satisfaisant de voir ces réponses pour avoir deux méthodes statiques pour définir et obtenir des attributs imbriqués. Ces solutions sont bien meilleures que l'utilisation d'arbres imbriqués. https://gist.github.com/hrldcpr/2012250

Voici ma mise en œuvre.

Utilisation :

Pour définir un attribut imbriqué, appelez sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5

Pour obtenir un attribut imbriqué, appelez gattr(my_dict, 1, 2)

def gattr(d, *attrs):
    """
    This method receives a dict and list of attributes to return the innermost value of the give dict       
    """
    try:
        for at in attrs:
            d = d[at]
        return d
    except(KeyError, TypeError):
        return None

def sattr(d, *attrs):
    """
    Adds "val" to dict in the hierarchy mentioned via *attrs
    For ex:
    sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
    This method creates necessary objects until it reaches the final depth
    This behaviour is also known as autovivification and plenty of implementation are around
    This implementation addresses the corner case of replacing existing primitives
    https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
    """
    for attr in attrs[:-2]:
        if type(d.get(attr)) is not dict:
            d[attr] = {}
        d = d[attr]
    d[attrs[-2]] = attrs[-1]

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