循环遍历所有嵌套字典值?

时间:2012-05-25 14:41:23

标签: python dictionary

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

13 个答案:

答案 0 :(得分:104)

正如Niklas所说,你需要递归,即你想要定义一个函数来打印你的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 :(得分:26)

由于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的自定义iteritems,在这种情况下这会失败。文档未指明iteritems需要Mapping;另一方面,sourceMapping类型提供iteritems方法。因此,对于自定义Mappings,请明确继承collections.Mapping

在Python 3中,有许多改进。从Python 3.3开始,抽象基类存在于collections.abc中。它们仍然保留在collections中以便向后兼容,但是在一个命名空间中将抽象基类放在一起会更好。因此,这会从abc导入collections。 Python 3.3还添加了yield from,它专为这些情况而设计。这不是空的句法糖;这可能会导致faster codecoroutines进行更明智的互动。

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 :(得分:21)

替代迭代解决方案:

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

答案 3 :(得分:20)

如果您编写自己的递归实现或使用堆栈的迭代等效项,则存在潜在问题。见这个例子:

    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 的实施也是如此。

同样地,你从 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'}}

&#34; {...}&#34; 是检测到周期的地方。

根据 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)

答案 4 :(得分: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'

如果您需要这样做,也可以很容易地修改它以跟踪前缀作为键的元组而不是字符串。

答案 5 :(得分:4)

这是pythonic的方法。此功能允许您循环遍历所有级别中的键值对。它不会将整个事物保存到内存中,而是在循环中遍历字典

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

答案 6 :(得分:2)

您的问题已经得到很好的回答,但我建议使用 isinstance(d, collections.Mapping) 而不是 isinstance(d, dict)。它适用于 dict()collections.OrderedDict()collections.UserDict()

通常正确的版本是:

def myprint(d):
    for k, v in d.items():
        if isinstance(v, collections.Mapping):
            myprint(v)
        else:
            print("{0} : {1}".format(k, v))

答案 7 :(得分: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

答案 8 :(得分: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)

答案 9 :(得分:1)

这里是Fred Foo对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]])

用于三级字典。

在访问多个密钥之前你需要知道级别的数量,并且级别的数量应该是不变的(在迭代值时可以添加一小部分脚本来检查嵌套级别的数量,但是我尚未看过这个)。

答案 10 :(得分:1)

我使用以下代码打印嵌套字典的所有值,同时考虑值可能是包含字典的列表。在将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

答案 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上测试了这个。

答案 12 :(得分:0)

这些答案仅适用于2级子词典。有关更多信息,请尝试:

nested_dict = {'dictA': {'key_1': 'value_1', 'key_1A': 'value_1A','key_1Asub1': {'Asub1': 'Asub1_val', 'sub_subA1': {'sub_subA1_key':'sub_subA1_val'}}},
                'dictB': {'key_2': 'value_2'},
                1: {'key_3': 'value_3', 'key_3A': 'value_3A'}}

def print_dict(dictionary):
    dictionary_array = [dictionary]
    for sub_dictionary in dictionary_array:
        if type(sub_dictionary) is dict:
            for key, value in sub_dictionary.items():
                print("key=", key)
                print("value", value)
                if type(value) is dict:
                    dictionary_array.append(value)



print_dict(nested_dict)