首页 文章

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

提问于
浏览
68
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'}

11 回答

  • 24

    正如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))
    
  • 5

    由于 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 ,在这种情况下,这将失败 . 文档并未表明 Mapping 需要 iteritems ;另一方面,sourceMapping 类型一个 iteritems 方法 . 因此,对于自定义 Mappings ,显式继承 collections.Mapping 以防万一 .

    在Python 3中,有许多改进 . 从Python 3.3开始,抽象基类存在于 collections.abc 中 . 它们仍然保留在 collections 中以便向后兼容,但是在一个命名空间中将抽象基类放在一起会更好 . 所以这从 collections 进口 abc . 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
    
  • 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))
    
  • 19

    如果您编写自己的递归实现或使用堆栈的迭代等效项,则有 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)
    
  • 14

    我编写的版本略有不同,可以跟踪到达目的地的密钥

    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'
    

    它也很容易修改它来跟踪前缀作为键的元组而不是字符串,如果你需要它那样 .

  • 93

    这是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
    
  • 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
    
  • 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)
    
  • 4

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

    对于三级字典 .

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

  • 1

    我发现这种方法更灵活一些,在这里你只需要提供发出键,值对的生成器函数,并且可以很容易地扩展到迭代列表 .

    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

    我使用以下代码打印嵌套字典的所有值,考虑到值可能是包含字典的列表 . 在将JSON文件解析为字典并需要快速检查其中的任何值是否为_1756833时,这对我很有用 .

    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
    

相关问题