¿Recorrer todos los valores del diccionario anidados?
Estoy intentando recorrer un diccionario e imprimir todos los pares clave-valor donde el valor no es un diccionario anidado. Si el valor es un diccionario, quiero acceder a él e imprimir sus pares clave-valor, etc.
Intenté esto. Pero sólo funciona para los dos primeros niveles. Necesito que funcione para cualquier número de niveles.
for k, v in d.iteritems():
if type(v) is dict:
for t, c in v.iteritems():
print "{0} : {1}".format(t, c)
También probé esto. Todavía solo imprime una cosa.
def printDict(d):
for k, v in d.iteritems():
if type(v) is dict:
printDict(v)
else:
print "{0} : {1}".format(k, v)
Caso de prueba completo:
Diccionario:
{u'xml': {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'},
u'port': u'11'}}
Resultado:
xml : {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'}, u'port': u'11'}
Como dijo Niklas, necesita recursividad, es decir, desea definir una función para imprimir su dictado, y si el valor es un dictado, desea llamar a su función de impresión usando este nuevo dictado.
Algo como :
def myprint(d):
for k, v in d.items():
if isinstance(v, dict):
myprint(v)
else:
print("{0} : {1}".format(k, v))
Existen problemas potenciales si escribe su propia implementación recursiva o el equivalente iterativo con pila. Vea este ejemplo:
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
En el sentido normal, el diccionario anidado será una estructura de datos similar a un árbol n-nario. Pero la definición no excluye la posibilidad de un borde transversal o incluso un borde posterior (por lo tanto, ya no es un árbol). Por ejemplo, aquí key2.2 se refiere al diccionario desde key1 , key2.3 apunta a todo el diccionario (borde posterior/ciclo). Cuando hay un borde posterior (ciclo), la pila/recursión se ejecutará infinitamente.
root<-------back edge
/ \ |
_key1 __key2__ |
/ / \ \ |
|->key1.1 key2.1 key2.2 key2.3
| / | |
| value1 value2 |
| |
cross edge----------|
Si imprime este diccionario con esta implementación de Scharron
def myprint(d):
for k, v in d.items():
if isinstance(v, dict):
myprint(v)
else:
print "{0} : {1}".format(k, v)
Verías este error:
> RuntimeError: maximum recursion depth exceeded while calling a Python object
Lo mismo ocurre con la implementación de senderle .
De manera similar, obtienes un bucle infinito con esta implementación 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))
Sin embargo, Python en realidad detecta ciclos en un diccionario anidado:
print dic
{'key2': {'key2.1': 'value2', 'key2.3': {...},
'key2.2': {'key1.1': 'value1'}}, 'key1': {'key1.1': 'value1'}}
"{...}" es donde se detecta un ciclo.
Según lo solicitado por Moondra esta es una forma de evitar ciclos (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)
Dado que a dict
es iterable, puede aplicar la clásica fórmula iterable de contenedor anidado a este problema con solo un par de cambios menores. Aquí hay una versión de Python 2 (ver 3 a continuación):
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
Prueba:
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)]
En Python 2, es posible crear una costumbre Mapping
que califique como Mapping
pero no contenga iteritems
, en cuyo caso esto fallará. Los documentos no indican que iteritems
sea necesario para un Mapping
; por otro lado, la fuente proporciona Mapping
un iteritems
método a los tipos. Entonces, para custom Mappings
, herede collections.Mapping
explícitamente por si acaso.
En Python 3, hay una serie de mejoras por realizar. A partir de Python 3.3, las clases base abstractas se encuentran en formato collections.abc
. También permanecen collections
por compatibilidad con versiones anteriores, pero es mejor tener nuestras clases base abstractas juntas en un espacio de nombres. Entonces esto se importa abc
desde collections
. Python 3.3 también agrega yield from
, que está diseñado precisamente para este tipo de situaciones. Esto no es azúcar sintáctico vacío; puede conducir a un código más rápido e interacciones más sensatas con las corrutinas .
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