for k, v in d.iteritems():
if type(v) is dict:
for t, c in v.iteritems():
print "{0} : {1}".format(t, c)
J'essaie de parcourir un dictionnaire et d'imprimer toutes les paires de valeurs clés où la valeur n'est pas un dictionnaire imbriqué. Si la valeur est un dictionnaire, je veux y aller et imprimer ses paires clé-valeur ... etc. De l'aide?
MODIFIER
Que dis-tu de ça? Il n’imprime toujours qu’une chose.
def printDict(d):
for k, v in d.iteritems():
if type(v) is dict:
printDict(v)
else:
print "{0} : {1}".format(k, v)
Cas de test complet
Dictionnaire:
{u'xml': {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'},
u'port': u'11'}}
Résultat:
xml : {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'}, u'port': u'11'}
Comme le dit Niklas, vous avez besoin de récursivité, c’est-à-dire que vous souhaitez définir une fonction pour imprimer votre dict, et si la valeur est un dict, vous souhaitez appeler votre fonction print à l’aide de ce nouveau dict.
Quelque chose comme :
def myprint(d):
for k, v in d.iteritems():
if isinstance(v, dict):
myprint(v)
else:
print "{0} : {1}".format(k, v)
Ou à partir de Python 3:
def myprint(d):
for k, v in d.items():
if isinstance(v, dict):
myprint(v)
else:
print("{0} : {1}".format(k, v))
Etant donné que dict
est itérable, vous pouvez appliquer la formule classique formule itérable par conteneur imbriqué à ce problème avec seulement quelques modifications mineures. Voici une version Python 2 (voir ci-dessous pour 3):
import collections
def nested_dict_iter(nested):
for key, value in nested.iteritems():
if isinstance(value, collections.Mapping):
for inner_key, inner_value in nested_dict_iter(value):
yield inner_key, inner_value
else:
yield key, value
Tester:
list(nested_dict_iter({'a':{'b':{'c':1, 'd':2},
'e':{'f':3, 'g':4}},
'h':{'i':5, 'j':6}}))
# output: [('g', 4), ('f', 3), ('c', 1), ('d', 2), ('i', 5), ('j', 6)]
Dans Python 2, Il est possible de créer une Mapping
personnalisée pouvant être qualifiée de Mapping
mais ne contient pas iteritems
, auquel cas cela échouera. La documentation n'indique pas que iteritems
est requis pour un Mapping
; d'autre part, le source donne Mapping
tape une méthode iteritems
. Donc, pour la coutume Mappings
, héritez de collections.Mapping
explicitement au cas où.
Dans Python 3, un certain nombre d'améliorations doivent être apportées. À partir de Python3.3, les classes de base abstraites résident dans collections.abc
. Ils restent dans collections
également pour des raisons de compatibilité ascendante, mais il est plus agréable de regrouper nos classes de base abstraites dans un seul espace de noms. Donc cela importe abc
de collections
. Python 3.3 ajoute également yield from
, conçu pour ce type de situations. Ce n'est pas un sucre syntaxique vide; cela peut conduire à code plus rapide et à des interactions plus sensibles avec coroutines .
from collections import abc
def nested_dict_iter(nested):
for key, value in nested.items():
if isinstance(value, abc.Mapping):
yield from nested_dict_iter(value)
else:
yield key, value
Solution itérative alternative:
def myprint(d):
stack = d.items()
while stack:
k, v = stack.pop()
if isinstance(v, dict):
stack.extend(v.iteritems())
else:
print("%s: %s" % (k, v))
Il y a des problèmes potentiels si vous écrivez votre propre implémentation récursive ou l'équivalent itératif avec pile. Voir cet exemple:
dic = {}
dic["key1"] = {}
dic["key1"]["key1.1"] = "value1"
dic["key2"] = {}
dic["key2"]["key2.1"] = "value2"
dic["key2"]["key2.2"] = dic["key1"]
dic["key2"]["key2.3"] = dic
Dans le sens normal, dictionnaire imbriqué sera un arbre n-nary comme structure de données. Mais la définition n'exclut pas la possibilité d'un bord en travers ou même d'un bord en arrière (donc plus un arbre). Par exemple, ici key2.2 correspond au dictionnaire car key1, key2.3 pointe vers le dictionnaire entier (bord arrière/cycle). Lorsqu'il y a un bord arrière (cycle), la pile/la récursivité s'exécutent indéfiniment.
root<-------back Edge
/ \ |
_key1 __key2__ |
/ / \ \ |
|->key1.1 key2.1 key2.2 key2.3
| / | |
| value1 value2 |
| |
cross Edge----------|
Si vous imprimez ce dictionnaire avec cette implémentation de Scharron
def myprint(d):
for k, v in d.items():
if isinstance(v, dict):
myprint(v)
else:
print "{0} : {1}".format(k, v)
Vous verriez cette erreur:
RuntimeError: maximum recursion depth exceeded while calling a Python object
Il en va de même avec l'implémentation de senderle .
De même, vous obtenez une boucle infinie avec cette implémentation de Fred Foo :
def myprint(d):
stack = list(d.items())
while stack:
k, v = stack.pop()
if isinstance(v, dict):
stack.extend(v.items())
else:
print("%s: %s" % (k, v))
Cependant, Python détecte réellement les cycles dans le dictionnaire imbriqué:
print dic
{'key2': {'key2.1': 'value2', 'key2.3': {...},
'key2.2': {'key1.1': 'value1'}}, 'key1': {'key1.1': 'value1'}}
"{...}" est l'endroit où un cycle est détecté.
Comme demandé par Moondra c'est une façon d'éviter les cycles (DFS):
def myprint(d):
stack = list(d.items())
visited = set()
while stack:
k, v = stack.pop()
if isinstance(v, dict):
if k not in visited:
stack.extend(v.items())
else:
print("%s: %s" % (k, v))
visited.add(k)
Version légèrement différente que j'ai écrite et qui garde une trace des clés le long du chemin pour y arriver
def print_dict(v, prefix=''):
if isinstance(v, dict):
for k, v2 in v.items():
p2 = "{}['{}']".format(prefix, k)
print_dict(v2, p2)
Elif isinstance(v, list):
for i, v2 in enumerate(v):
p2 = "{}[{}]".format(prefix, i)
print_dict(v2, p2)
else:
print('{} = {}'.format(prefix, repr(v)))
Sur vos données, ça va imprimer
data['xml']['config']['portstatus']['status'] = u'good'
data['xml']['config']['target'] = u'1'
data['xml']['port'] = u'11'
Il est également facile de le modifier pour suivre le préfixe sous forme de tuple de clés plutôt que de chaîne si vous en avez besoin.
Voici comment pythonique le faire. Cette fonction vous permettra de parcourir la paire clé-valeur dans tous les niveaux. Il n'enregistre pas le tout dans la mémoire mais passe en revue le dict lorsque vous le parcourez.
def recursive_items(dictionary):
for key, value in dictionary.items():
if type(value) is dict:
yield (key, value)
yield from recursive_items(value)
else:
yield (key, value)
a = {'a': {1: {1: 2, 3: 4}, 2: {5: 6}}}
for key, value in recursive_items(a):
print(key, value)
Impressions
a {1: {1: 2, 3: 4}, 2: {5: 6}}
1 {1: 2, 3: 4}
1 2
3 4
2 {5: 6}
5 6
Solution itérative comme alternative:
def traverse_nested_dict(d):
iters = [d.iteritems()]
while iters:
it = iters.pop()
try:
k, v = it.next()
except StopIteration:
continue
iters.append(it)
if isinstance(v, dict):
iters.append(v.iteritems())
else:
yield k, v
d = {"a": 1, "b": 2, "c": {"d": 3, "e": {"f": 4}}}
for k, v in traverse_nested_dict(d):
print k, v
Voici une version modifiée de la réponse de Fred Foo pour Python 2. Dans la réponse d'origine, seul le niveau d'imbrication le plus profond est généré. Si vous produisez les clés sous forme de listes, vous pouvez les conserver pour tous les niveaux. Vous devez toutefois référencer une liste de listes pour les référencer.
Voici la fonction:
def NestIter(nested):
for key, value in nested.iteritems():
if isinstance(value, collections.Mapping):
for inner_key, inner_value in NestIter(value):
yield [key, inner_key], inner_value
else:
yield [key],value
Pour référencer les clés:
for keys, vals in mynested:
print(mynested[keys[0]][keys[1][0]][keys[1][1][0]])
pour un dictionnaire à trois niveaux.
Vous devez connaître le nombre de niveaux avant d'accéder à plusieurs clés et le nombre de niveaux doit être constant (il est peut-être possible d'ajouter un petit script pour vérifier le nombre de niveaux d'imbrication lors de l'itération de valeurs, mais je ne l'ai pas déjà. encore regardé cela).
Une solution alternative pour travailler avec des listes basées sur la solution de Scharron
def myprint(d):
my_list = d.iteritems() if isinstance(d, dict) else enumerate(d)
for k, v in my_list:
if isinstance(v, dict) or isinstance(v, list):
myprint(v)
else:
print u"{0} : {1}".format(k, v)
J'utilise le code suivant pour imprimer toutes les valeurs d'un dictionnaire imbriqué, en tenant compte du fait que la valeur peut être une liste contenant des dictionnaires. Cela m'a été utile lors de l'analyse d'un fichier JSON dans un dictionnaire et de la nécessité de vérifier rapidement si l'une de ses valeurs est None
.
d = {
"user": 10,
"time": "2017-03-15T14:02:49.301000",
"metadata": [
{"foo": "bar"},
"some_string"
]
}
def print_nested(d):
if isinstance(d, dict):
for k, v in d.items():
print_nested(v)
Elif hasattr(d, '__iter__') and not isinstance(d, str):
for item in d:
print_nested(item)
Elif isinstance(d, str):
print(d)
else:
print(d)
print_nested(d)
Sortie:
10
2017-03-15T14:02:49.301000
bar
some_string
Je trouve cette approche un peu plus flexible. Ici, vous fournissez simplement une fonction de générateur qui émet des paires clé/valeur et peut être facilement étendue pour parcourir également des listes.
def traverse(value, key=None):
if isinstance(value, dict):
for k, v in value.items():
yield from traverse(v, k)
else:
yield key, value
Ensuite, vous pouvez écrire votre propre fonction myprint
, puis imprimer ces paires de valeurs de clé.
def myprint(d):
for k, v in traverse(d):
print(f"{k} : {v}")
Un examen:
myprint({
'xml': {
'config': {
'portstatus': {
'status': 'good',
},
'target': '1',
},
'port': '11',
},
})
Sortie:
status : good
target : 1
port : 11
J'ai testé cela sur Python 3.6.