Цикл через все вложенные значения словаря?


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 62

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