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 existe 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
python
list
dictionary
kolergy
la source
la source
Réponses:
Utilisez
reduce()
pour parcourir le dictionnaire:et réutiliser
getFromDict
pour trouver l'emplacement pour stocker la valeur poursetInDict()
:Tous les éléments sauf le dernier
mapList
sont nécessaires pour trouver le dictionnaire «parent» auquel ajouter la valeur, puis utilisez le dernier élément pour définir la valeur sur la bonne clé.Démo:
Notez que le guide de style Python PEP8 prescrit des noms snake_case pour les fonctions . Ce qui précède fonctionne aussi bien pour les listes que pour un mélange de dictionnaires et de listes, donc les noms devraient vraiment être
get_by_path()
etset_by_path()
:la source
try:
,except (KeyError, IndexError): return default_value
autour de lareturn
ligne courante .dict.get()
change la sémantique, car elle renvoieNone
plutôt que relanceKeyError
pour les noms manquants. Tous les noms suivants déclenchent alors unAttributeError
.operator
est une bibliothèque standard, il n'est pas nécessaire de l'éviter ici.from functools import reduce
.for
boucle. Voir la citation de Quoi de neuf dans Python 3.0 .KeyError
) - voir la réponse de @ eafit pour une solutionAlors pourquoi ne pas utiliser la méthode suggérée de la question de kolergy pour obtenir une valeur:
Et le code de la réponse de @ eafit pour définir une valeur:
Les deux fonctionnent directement en python 2 et 3
la source
getFromDict
peut potentiellement détruire l'appelantdataDict
. Je voudrais d'copy.deepcopy(dataDict)
abord. Bien sûr, (comme écrit) ce comportement est souhaité dans la deuxième fonction.L'utilisation de la réduction est intelligente, mais la méthode set de l'OP peut avoir des problèmes si les clés parentes n'existent pas dans le dictionnaire imbriqué. Comme il s'agit du premier article SO que j'ai vu pour ce sujet dans ma recherche Google, j'aimerais l'améliorer légèrement.
La méthode set dans ( Définition d'une valeur dans un dictionnaire python imbriqué à partir d'une liste d'indices et de valeurs ) semble plus robuste aux clés parentales manquantes. Pour le copier:
En outre, il peut être pratique d'avoir une méthode qui traverse l'arborescence des clés et obtenir tous les chemins de clé absolus, pour lesquels j'ai créé:
Une utilisation de celui-ci est de convertir l'arborescence imbriquée en un DataFrame pandas, en utilisant le code suivant (en supposant que toutes les feuilles du dictionnaire imbriqué ont la même profondeur).
la source
nested_set
?Cette bibliothèque peut être utile: https://github.com/akesterson/dpath-python
la source
Que diriez-vous d'utiliser des fonctions récursives?
Pour obtenir une valeur:
Et pour définir une valeur:
la source
Style pur Python, sans aucune importation:
Production
la source
Une autre manière si vous ne souhaitez pas déclencher d'erreurs si l'une des clés est absente (pour que votre code principal puisse s'exécuter sans interruption):
Dans ce cas, si l'une des clés d'entrée n'est pas présente, None est renvoyé, ce qui peut être utilisé comme vérification de votre code principal pour effectuer une tâche alternative.
la source
Au lieu de prendre une performance à chaque fois que vous souhaitez rechercher une valeur, que diriez-vous d'aplatir le dictionnaire une fois, puis de rechercher simplement la clé comme
b:v:y
De cette façon, vous pouvez simplement rechercher des éléments en utilisant
flat_dict['b:v:y']
ce qui vous donnera1
.Et au lieu de parcourir le dictionnaire à chaque recherche, vous pourrez peut-être accélérer cela en aplatissant le dictionnaire et en enregistrant la sortie de sorte qu'une recherche à partir d'un démarrage à froid signifierait charger le dictionnaire aplati et simplement effectuer une recherche clé / valeur sans traversée.
la source
Résolu cela avec la récursivité:
En utilisant votre exemple:
la source
Que diriez-vous de vérifier puis de définir l'élément dict sans traiter tous les index deux fois?
Solution:
Exemple de workflow:
Tester
la source
Très tard à la fête, mais poster au cas où cela pourrait aider quelqu'un à l'avenir. Pour mon cas d'utilisation, la fonction suivante a fonctionné le mieux. Fonctionne pour extraire n'importe quel type de données du dictionnaire
dict est le dictionnaire contenant notre valeur
list est une liste des "étapes" vers notre valeur
la source
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.
Usage :
Pour définir un appel d'attribut imbriqué
sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5
Pour obtenir un appel d'attribut imbriqué
gattr(my_dict, 1, 2)
la source
Je vous suggère d'utiliser
python-benedict
pour accéder aux éléments imbriqués en utilisant keypath.Installez-le en utilisant
pip
:Ensuite:
Voici la documentation complète: https://github.com/fabiocaccamo/python-benedict
la source
Si vous souhaitez également pouvoir travailler avec des json arbitraires, y compris des listes imbriquées et des dictionnaires, et gérer correctement les chemins de recherche non valides, voici ma solution:
la source
une méthode pour concaténer des chaînes:
la source
En étendant l'approche de @DomTomCat et d'autres, ces setter et mappeur fonctionnels (c'est-à-dire retournent des données modifiées via deepcopy sans affecter l'entrée) fonctionnent pour imbriqués
dict
etlist
.setter:
mappeur:
la source
Vous pouvez utiliser la
eval
fonction en python.Explication
Pour votre exemple de requête:
maplist = ["b", "v", "y"]
nestq
sera"nest['b']['v']['y']"
oùnest
trouve le dictionnaire imbriqué.La
eval
fonction intégrée exécute la chaîne donnée. Cependant, il est important de faire attention aux éventuelles vulnérabilités résultant de l'utilisation de laeval
fonction. La discussion peut être trouvée ici:Dans la
nested_parse()
fonction, je me suis assuré qu'aucun__builtins__
global n'est disponible et que seule la variable locale disponible est lenest
dictionnaire.la source
Vous pouvez utiliser pydash:
https://pydash.readthedocs.io/en/latest/api.html
la source