2012-05-25 2 views
54
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) 

Полного тест Case

Словарь:

{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

Похоже, вы хотите рекурсию, но описание не является достаточно ясным, чтобы быть конечно. Как насчет некоторых примеров ввода/вывода? Кроме того, что не так с вашим кодом? –

+0

Будет ли только один уровень гнездования или произвольная глубина гнездования? – senderle

+0

Глубина гнездования может варьироваться – Takkun

ответ

75

По словам Никласа, вам нужна рекурсия, то есть вы хотите, чтобы определить функцию для печати ваш 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)) 
+1

ha, я просто написал именно это, и он все еще просто печатает первый словарь, а не что-то в нем. – Takkun

+1

@Takkun: Тогда вы делаете что-то еще неправильно. Код правильный (но он должен использовать 'isinstance (v, dict)') –

+1

myprint ({"a": 1, "b": 2, "c": {"d": 3, "e": { "F": 4}}}) : с 1 F: 4 д: 3 б: 2 не то, что можно ожидать? – Scharron

18

Поскольку dict является итерацию, вы можете применить классический nested container iterable formula к этой проблеме лишь несколько незначительных изменений. Вот версия 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 но Безразлично 't содержит iteritems, и в этом случае это не удастся. Документы не указывают, что iteritems требуется для Mapping; с другой стороны, source дает Mapping тип iteritems. Поэтому для пользовательских Mappings наследуйте от collections.Mapping явно на всякий случай.

В Python 3 есть ряд улучшений. Начиная с Python 3.3, абстрактные базовые классы живут в collections.abc. Они также остаются в collections для обратной совместимости, но лучше всего использовать наши абстрактные базовые классы в одном пространстве имен. Так что импорт abc от collections. Python 3.3 также добавляет yield from, который предназначен только для подобных ситуаций. Это не пустой синтаксический сахар; это может привести к faster code и более разумным взаимодействиям с coroutines.

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 
+2

'isinstance (item, collections.Iterable)' не является гарантией для 'hasattr (item," iteritems ")'. Лучше проверить для 'collections.Mapping'. –

+1

@larsmans, вы совершенно правы, конечно. Я думал, что использование 'Iterable' сделает это решение более обобщенным, забыв, что, очевидно, итерации не обязательно имеют' iteritems'. – senderle

+0

+1 к этому ответу, потому что это общее решение, которое работает для этой проблемы, но это не ограничивается только печатью значений. @Takkun, вы должны обязательно рассмотреть этот вариант. В конечном итоге вам нужно больше, чем просто распечатать значения. –

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 
+0

Ну, теперь это не очень элегантно для меня ... –

+0

@NiklasB. Но это меньше занимает пространство ... – schlamar

+0

Как это? Big O должен быть одинаковым (это 'O (глубина)' для рекурсивного решения. То же самое относится к этой версии, если я правильно думаю). –

15

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

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)) 
+0

Да, именно так я и представлял себе это. Благодарю. Таким образом, преимущество этого заключается в том, что он не будет переполнять стек для чрезвычайно глубоких вложений? Или есть что-то еще? –

+0

@NiklasB .: Да, это первое преимущество. Кроме того, эту версию можно легко адаптировать к различным ордерам обмена, заменив стек («список») на «deque» или даже на приоритетную очередь. –

+0

Да, имеет смысл. Спасибо и счастливое кодирование :) –

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

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

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' 

Это также легко модифицировать его для отслеживания префикс в виде кортеж ключей, а не строка, если вам это нужно.

9

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

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

Кроме того, вы получите бесконечный цикл с этой реализации из Фред Foo:

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'}} 

«{... } « - где обнаружен цикл.

+0

Итак, как бы вы реализовали итерационное решение? – dreftymac

+1

@dreftymac Я хотел бы добавить посещаемый набор для ключей, чтобы не идти циклы: определения функции myprint (D): стек = d.Items() посещаемых = множество() в то время как стек: к, v = stack.pop() если isinstance (v, ДИКТ): \t, если к не посетил: \t \t stack.extend (v.iteritems()) еще: \t печати ("% S:% S" % (к, v)) \t visited.add (k) ' – tengr

1

Вот измененная версия ответа Фреда Фу на 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]]) 

для словаря три уровня.

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

3

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

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 
Смежные вопросы