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

75
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'}
@ Jan-PhilipGehrcke: Реализация алгоритмов на древовидной структуре данных без рекурсии - простое самоубийство. Niklas B.
@NiklasB., Re: & quot; suicide & quot ;: Я только что реализовал итеративную версию алгоритма Scharron'а, и он всего на две строки длиннее и все еще довольно прост для понимания. Кроме того, перевод рекурсии в итерацию часто является обязательным требованием при переходе от деревьев к общим графам. Fred Foo
@Takkun: вы используетеdict как имя переменной. Никогда не делайте этого (вот почему это не получается). Niklas B.
Похоже, вы хотите рекурсии, но описание не достаточно ясно, чтобы быть уверенным. Как насчет примера ввода / вывода? Кроме того, что не так с вашим кодом? Niklas B.
В Python есть фиксированный предел рекурсии:docs.python.org/library/sys.html#sys.setrecursionlimit Jan-Philip Gehrcke

Ваш Ответ

11   ответов
1

основанное на решении 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)
20

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))
@NiklasB .: да, это первое преимущество. Кроме того, эта версия может быть легко адаптирована к различным порядкам обхода путем замены стека (list)deque или даже приоритетная очередь.
Да, но это решение занимает больше места, чем мое и рекурсивное.
Да, имеет смысл. Спасибо и счастливого кодирования :)
@ ms4py: для развлечения я создалa benchmark, На моем компьютере рекурсивная версия самая быстрая, а larsmans - вторая для всех трех тестовых словарей. Версия, использующая генераторы, является относительно медленной, как и ожидалось (потому что она должна делать много манипуляций с различными контекстами генератора)
Да, вот как я себе это представлял. Благодарю. Таким образом, преимущество этого состоит в том, что он не переполнит стек для чрезвычайно глубоких вложений? Или есть что-то еще?
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.

1

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

Вот эта функция:

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

для трехуровневого словаря.

Прежде чем получить доступ к нескольким ключам, необходимо знать количество уровней, а количество уровней должно быть постоянным (возможно, можно добавить небольшой кусочек сценария для проверки количества уровней вложенности при итерации по значениям, но у меня нет пока посмотрел на это).

25

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 этоmight можно создать кастом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
isinstance(item, collections.Iterable) нет гарантии дляhasattr(item, "iteritems"), Проверка наcollections.Mapping лучше.
+1 к этому ответу, потому что это общее решение, которое работает для этой проблемы, но оно не ограничивается простой печатью значений. @ Таккун, вы обязательно должны рассмотреть этот вариант. В долгосрочной перспективе вам нужно больше, чем просто печатать значения.
@larsmans, ты, конечно, совершенно прав. Я думал, что с помощьюIterable сделало бы это решение более обобщенным, забыв, что, очевидно, итерируемыеiteritems.
@ Seanny123, спасибо, что обратили на это мое внимание. Фактически, Python 3 меняет картину несколькими способами - я собираюсь переписать это как версию, которая использует новуюyield from синтаксис.
4

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

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
18

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

    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 дерево, подобное структуре данных. Но определениеdoesn't exclude возможность поперечного края или даже заднего края (таким образом, больше не дерево). Например, здесь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)
тогда как бы вы реализовали итеративное решение?
обновлено. Он был написан на основе Python2
@dreftymac Я бы добавил посещенный набор ключей, чтобы избежать циклов: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)
Спасибо за указание на это. Не могли бы вы включить свой код в ответ. Я думаю, что это завершает ваш отличный ответ.
Для Python3 используйтеlist(d.items()) какd.items() возвращает представление, а не список, и использоватьv.items() вместоv.iteritems()
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
У меня есть много подобных проблем здесьstackoverflow.com/questions/50642922/…, Есть ли способ найти последний элемент списка словаря, удалить его и затем поднять уровень вверх? Если не удалить, я хочу создать список, где последним элементом является глубина данных, поэтому я переворачиваю список и удаляю
1

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
@NiklasB. Нет, потому что кадр стека здесь только iter, а для рекурсивного решения кадр стека имеет iter, счетчик программ, переменную среду и т. Д ...
Потому что итеративное решение не копирует стек для каждого словаря ...
@NiklasB. Но это занимает меньше места ...
Как так? Большой O должен быть одинаковым (этоO(depth) для рекурсивного решения. То же самое относится и к этой версии, если я правильно думаю).
Что ж, теперь это выглядит не очень элегантно для меня ...
99

вам нужна рекурсия, то есть вы хотите определить функцию для печати вашего 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))
небольшое улучшение. добавить print (k) перед вызовом myprint (v).
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'

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

Как добавить вывод в список?

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