Пропустить все вложенные значения словаря?
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.iteritems():
if isinstance(v, dict):
myprint(v)
else:
print "{0} : {1}".format(k, v)
Или для Python 3 и далее:
def myprint(d):
for k, v in d.items():
if isinstance(v, dict):
myprint(v)
else:
print("{0} : {1}".format(k, v))
Ответ 2
Так как dict
iserable, вы можете применить классическую вложенную формулу итерабельной контейнера к этой проблеме только с несколькими незначительными изменениями. Здесь версия 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
, и в этом случае это не удастся. Документы не указывают, что для Mapping
требуются iteritems
; с другой стороны, источник дает 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
Ответ 3
Альтернативное итерационное решение:
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))
Ответ 4
Существуют потенциальные проблемы, если вы пишете собственную рекурсивную реализацию или итеративный эквивалент со стеком. См. Этот пример:
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-nary, такое как структура данных. Но определение не исключает возможности перекрестного края или даже заднего края (таким образом, больше не дерева). Например, здесь 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'}}
"{...}" - это где обнаружен цикл.
По просьбе Moondra это способ избежать циклов (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)
Ответ 5
Немного другая версия, которую я написал, отслеживает ключи по пути, чтобы добраться туда
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)))
В ваших данных он напечатает
data['xml']['config']['portstatus']['status'] = u'good'
data['xml']['config']['target'] = u'1'
data['xml']['port'] = u'11'
Также легко изменить его, чтобы отслеживать префикс как кортеж ключей, а не строку, если вам это нужно.
Ответ 6
Вот питонический способ сделать это. Эта функция позволит вам перебирать пару "ключ-значение" на всех уровнях. Он не сохраняет все это в памяти, а проходит через dict, когда вы его проецируете
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)
Печать
a {1: {1: 2, 3: 4}, 2: {5: 6}}
1 {1: 2, 3: 4}
1 2
3 4
2 {5: 6}
5 6
Ответ 7
Итеративное решение в качестве альтернативы:
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
Ответ 8
Альтернативное решение для работы со списками на основе решения Шаррона
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)
Ответ 9
Здесь приведена модифицированная версия ответа Fred Foo для Python 2. В исходном ответе выводится только самый глубокий уровень вложенности. Если вы выводите ключи в виде списков, вы можете хранить ключи для всех уровней, хотя для их ссылки вам необходимо ссылаться на список списков.
Здесь функция:
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
Для ссылки на клавиши:
for keys, vals in mynested:
print(mynested[keys[0]][keys[1][0]][keys[1][1][0]])
для трехуровневого словаря.
Вам нужно знать количество уровней перед доступом к нескольким клавишам, а количество уровней должно быть постоянным (возможно, можно добавить небольшой бит script, чтобы проверить количество уровней вложенности при повторении значений, но я еще не посмотрел на это).
Ответ 10
Я считаю этот подход более гибким, здесь вы просто предоставляете функцию генератора, которая испускает пары ключей, значений и может быть легко расширена, чтобы также перебирать списки.
def traverse(value, key=None):
if isinstance(value, dict):
for k, v in value.items():
yield from traverse(v, k)
else:
yield key, value
Затем вы можете написать свою собственную функцию myprint
, а затем напечатать эти пары значений ключа.
def myprint(d):
for k, v in traverse(d):
print(f"{k} : {v}")
Тест:
myprint({
'xml': {
'config': {
'portstatus': {
'status': 'good',
},
'target': '1',
},
'port': '11',
},
})
Выход:
status : good
target : 1
port : 11
Я тестировал это на Python 3.6.
Ответ 11
Я использую следующий код для печати всех значений вложенного словаря с учетом того, где это значение может быть списком, содержащим словари. Это было полезно для меня при анализе JSON файла в словаре и необходимости быстро проверить, нет ли его значений 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)
Выход:
10
2017-03-15T14:02:49.301000
bar
some_string