Цикл через все вложенные значения словаря?
for k, v in d.iteritems():
if type(v) is dict:
for t, c in v.iteritems():
print "{0} : {1}".format(t, c)
Я пытаюсь перебрать словарь и распечатать все пары значений ключей, где значение не является вложенным словарем. Если значение является словарем, я хочу войти в него и распечатать его пары ключевых значений...так далее. Какая-нибудь помощь?
EDIT
как насчет этого? Он все еще печатает только одну вещь.
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'}
11 ответов:
Как сказал Никлас, вам нужна рекурсия, т. е. вы хотите определить функцию для печати вашего 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))
С 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
is требуется для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
альтернативные итерационное решение:
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))
здесь потенциальные проблемы если вы пишете свою собственную рекурсивную реализацию или итерационный эквивалент со стеком. См. этот пример:
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-ным деревом, подобным структуре данных. Но определение не исключает возможность перекрестного края или даже заднего края (таким образом, больше не дерево). Например, здесь ключ2.2 держит к словарю от key1, ключ2.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.iteritems(): if isinstance(v, dict): myprint(v) else: print "{0} : {1}".format(k, v)
вы увидите эту ошибку:
RuntimeError: maximum recursion depth exceeded while calling a Python object
то же самое происходит с реализации от senderle.
аналогично, вы получаете бесконечный цикл с этой реализации от Фред ФОО:
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 фактически обнаруживает циклы во вложенном словаре:
print dic {'key2': {'key2.1': 'value2', 'key2.3': {...}, 'key2.2': {'key1.1': 'value1'}}, 'key1': {'key1.1': 'value1'}}
" {...}" где обнаружен цикл.
по просьбе Moondra это способ избежать циклов (DFS):
def myprint(d): stack = d.items() visited = set() while stack: k, v = stack.pop() if isinstance(v, dict): if k not in visited: stack.extend(v.iteritems()) else: print("%s: %s" % (k, v)) visited.add(k)
вот это подходящие для Python способ сделать это. Эта функция позволит вам перебирать пары ключ-значение на всех уровнях. Он не сохраняет все это в памяти, а скорее проходит через 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
немного другая версия я написал, что отслеживает ключи по пути, чтобы добраться туда
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'
также легко изменить его, чтобы отслеживать префикс как кортеж ключей, а не строку, если вам это нужно.
итерационное решение в качестве альтернативы:
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
альтернативное решение для работы со списками на основе решения Шаррона
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)
вот модифицированная версия ответа Фреда Фу для 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]])
для трехуровневого словаря.
вы должны знать количество уровней, прежде чем доступ к нескольким ключам и количество уровней должны быть постоянными (возможно, можно добавить небольшой бит скрипта, чтобы проверить количество уровней вложенности при повторении значений, но я еще не смотрел на это).
Я нахожу этот подход немного более гибким, здесь вы просто предоставляете функцию генератора, которая выдает пары ключей, значений и может быть легко расширена, чтобы также перебирать списки.
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.
Я использую следующий код для печати всех значений вложенного словаря, принимая во внимание, где значение может быть список, содержащий словари. Это было полезно для меня при разборе файла 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