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

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'}
69 голосов | спросил Takkun 25 Maypm12 2012, 18:41:23

10 ответов


0

Как сказал Никлас, вам нужна рекурсия, то есть вы хотите определить функцию для печати вашего 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))
ответил Scharron 25 Maypm12 2012, 18:47:57
0

Поскольку 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
ответил senderle 25 Maypm12 2012, 18:52:40
0

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

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))
ответил Fred Foo 25 Maypm12 2012, 19:22:21
0

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

    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 .

Точно так же вы получаете бесконечный цикл с этой реализацией от Фреда Фу :

    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)
ответил tengr 4 Maypm16 2016, 14:45:26
0

Немного другая версия, которую я написал, которая отслеживает ключи по пути, чтобы туда добраться

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'

Также легко изменить его, чтобы отслеживать префикс в виде набора ключей, а не строки, если вам это нужно.

ответил Ehsan Kia 13 FebruaryEurope/MoscowbSat, 13 Feb 2016 15:55:58 +0300000000pmSat, 13 Feb 2016 15:55:58 +030016 2016, 15:55:58
0

Вот питонский способ сделать это. Эта функция позволит вам пройти через пару ключ-значение на всех уровнях. Это не сохраняет все это в памяти, а скорее проходит через диктовку, пока вы проходите через нее

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
ответил Dmitry Torba 30 PM00000090000001431 2016, 21:03:14
0

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

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
ответил schlamar 25 Maypm12 2012, 19:17:11
0

Альтернативное решение для работы со списками на основе решения Scharron

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)
ответил Gabriel 1 +03002015-10-01T00:37:11+03:00312015bEurope/MoscowThu, 01 Oct 2015 00:37:11 +0300 2015, 00:37:11
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.

ответил sirex 4 AMpWed, 04 Apr 2018 10:16:01 +030016Wednesday 2018, 10:16:01
0

Я использую следующий код для печати всех значений вложенного словаря, учитывая, где значение может быть списком, содержащим словари. Это было полезно для меня при анализе файла 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
ответил sigma 19 PMpThu, 19 Apr 2018 16:28:18 +030028Thursday 2018, 16:28:18

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132