Вопрос-Ответ

Loop through all nested dictionary values?

Перебирать все вложенные значения словаря?

Я пытаюсь перебрать словарь и распечатать все пары ключ-значение, где значение не является вложенным словарем. Если значение является словарем, я хочу войти в него и распечатать его пары ключ-значение и т.д.

Я пробовал это. Но это работает только для первых двух уровней. Мне нужно, чтобы это работало для любого количества уровней.

for k, v in d.iteritems():
if type(v) is dict:
for t, c in v.iteritems():
print "{0} : {1}".format(t, c)

Я тоже пробовал это. Он по-прежнему печатает только одно.

def printDict(d):
for k, v in d.iteritems():
if type(v) is dict:
printDict(v)
else:
print "{0} : {1}".format(k, v)

Полный тестовый пример:

Словарь:

{u'xml': {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'},
u'port': u'11'}}

Результат:

xml : {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'}, u'port': u'11'}
Переведено автоматически
Ответ 1

Как сказал Никлас, вам нужна рекурсия, т. Е. вы хотите определить функцию для печати вашего dict , и если значение является dict , вы хотите вызвать свою функцию печати, используя этот новый dict.

Что-то вроде :

def myprint(d):
for k, v in d.items():
if isinstance(v, dict):
myprint(v)
else:
print("{0} : {1}".format(k, v))
Ответ 2

Существуют потенциальные проблемы, если вы пишете свою собственную рекурсивную реализацию или итеративный эквивалент со stack . Смотрите этот пример.:

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

В обычном смысле вложенный словарь будет представлять собой n-n-ную древовидную структуру данных. Но определение не исключает возможности перекрестного ребра или даже заднего ребра (таким образом, это больше не дерево). Например, здесь key2.2 относится к словарю из key1, key2.3 указывает на весь словарь (задний край / цикл). При наличии обратного ребра (цикла) стек / рекурсия будет выполняться бесконечно.

            root<-------back edge
/ \ |
_key1 __key2__ |
/ / \ \ |
|->key1.1 key2.1 key2.2 key2.3
| / | |
| value1 value2 |
| |
cross edge----------|

Если вы распечатаете этот словарь с этой реализацией от Scharron

def myprint(d):
for k, v in d.items():
if isinstance(v, dict):
myprint(v)
else:
print "{0} : {1}".format(k, v)

Вы увидите эту ошибку:

> RuntimeError: maximum recursion depth exceeded while calling a Python object

То же самое касается реализации от senderle.

Аналогично, вы получаете бесконечный цикл с этой реализацией от 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))

Однако Python фактически обнаруживает циклы во вложенном словаре:

print dic
{'key2': {'key2.1': 'value2', 'key2.3': {...},
'key2.2': {'key1.1': 'value1'}}, 'key1': {'key1.1': 'value1'}}

"{...}" - это место, где обнаруживается цикл.

По запросу Moundra это способ избежать циклов (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)
Ответ 3

Поскольку a dict является итерабельным, вы можете применить классическую формулу итерации вложенного контейнера к этой проблеме всего с парой незначительных изменений. Вот версия Python 2 (см. Ниже 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

Тест:

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)]

В Python 2 может быть возможно создать пользовательский параметр, Mapping который квалифицируется как Mapping но не содержит iteritems, и в этом случае это приведет к сбою. В документах не указано, что iteritems требуется для Mapping; с другой стороны, источник предоставляет Mapping типам iteritems метод. Итак, для пользовательского Mappings наследования от collections.Mapping явно на всякий случай.

В Python 3 необходимо внести ряд улучшений. Начиная с Python 3.3, абстрактные базовые классы существуют в collections.abc. Они также остаются в collections для обратной совместимости, но лучше, когда наши абстрактные базовые классы собраны в одном пространстве имен. Итак, это импортируется abc из collections. Python 3.3 также добавляет yield from, который предназначен именно для таких ситуаций. Это не пустой синтаксический сахар; это может привести к более быстрому коду и более разумному взаимодействию с сопрограммами.

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
Ответ 4

Альтернативное итеративное решение:

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))
python dictionary