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

Tak*_*kun 93 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)
Run Code Online (Sandbox Code Playgroud)

我正在尝试遍历字典并打印出值不是嵌套字典的所有键值对.如果值是字典,我想进入它并打印出其键值对...等.有帮助吗?

编辑

这个怎么样?它仍然只打印一件事.

def printDict(d):
    for k, v in d.iteritems():
        if type(v) is dict:
            printDict(v)
        else:
            print "{0} : {1}".format(k, v)
Run Code Online (Sandbox Code Playgroud)

完整测试案例

字典:

{u'xml': {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'},
      u'port': u'11'}}
Run Code Online (Sandbox Code Playgroud)

结果:

xml : {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'}, u'port': u'11'}
Run Code Online (Sandbox Code Playgroud)

Sch*_*ron 124

正如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)
Run Code Online (Sandbox Code Playgroud)

或者对于Python 3以上版本:

def myprint(d):
  for k, v in d.items():
    if isinstance(v, dict):
      myprint(v)
    else:
      print("{0} : {1}".format(k, v))
Run Code Online (Sandbox Code Playgroud)

  • 这不适用于列表。它回答了这个问题,但我认为提问者隐含地要求一些能够正确遍历你的字典中的字典列表的东西,而这个实现中没有涵盖这一点。 (4认同)
  • 小改进。在调用myprint(v)之前添加print(k)。 (2认同)

sen*_*rle 28

由于a 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
Run Code Online (Sandbox Code Playgroud)

测试:

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)]
Run Code Online (Sandbox Code Playgroud)

在Python 2,这或许可以创建自定义的Mapping有资格的Mapping,但不包含iteritems,在这种情况下,这将失败.文档并未表明iteritemsa是必需的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
Run Code Online (Sandbox Code Playgroud)

  • `isinstance(item,collections.Iterable)`不能保证`hasattr(item,"iteritems")`.检查`collections.Mapping`是更好的. (2认同)
  • @Seanny123,感谢您提请我注意这一点。事实上,Python 3 在几个方面改变了这一情况——我将把它重写为使用新的“yield from”语法的版本。 (2认同)

ten*_*ngr 25

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

    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
Run Code Online (Sandbox Code Playgroud)

在通常意义上,嵌套字典将是一个像数据结构一样的n-nary树.但该定义并未排除交叉边缘甚至后边缘(因此不再是树)的可能性.例如,这里key2.2key1保持字典,key2.3指向整个字典(后边缘/循环).当存在后沿(循环)时,堆栈/递归将无限运行.

                          root<-------back edge
                        /      \           |
                     _key1   __key2__      |
                    /       /   \    \     |
               |->key1.1 key2.1 key2.2 key2.3
               |   /       |      |
               | value1  value2   |
               |                  | 
              cross edge----------|
Run Code Online (Sandbox Code Playgroud)

如果您使用Scharron的此实现打印此字典

    def myprint(d):
      for k, v in d.items():
        if isinstance(v, dict):
          myprint(v)
        else:
          print "{0} : {1}".format(k, v)
Run Code Online (Sandbox Code Playgroud)

你会看到这个错误:

    RuntimeError: maximum recursion depth exceeded while calling a Python object
Run Code Online (Sandbox Code Playgroud)

来自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))
Run Code Online (Sandbox Code Playgroud)

但是,Python实际上会检测嵌套字典中的循环:

    print dic
    {'key2': {'key2.1': 'value2', 'key2.3': {...}, 
       'key2.2': {'key1.1': 'value1'}}, 'key1': {'key1.1': 'value1'}}
Run Code Online (Sandbox Code Playgroud)

"{...}"是检测到循环的地方.

根据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)
Run Code Online (Sandbox Code Playgroud)

  • 回复:`visited.add(k)`:看起来使用键来检查字典是否已经被遍历不是一个好主意。相同的键名称可以在层次结构中的其他地方使用,我们最终将跳过这些。我们应该使用该值。 (3认同)
  • @codeforester 在任何特定的“dict”中不是唯一的键吗?“visited”堆栈不是全局的。为每个字典创建一个新的“visited”实例。所以我认为这段代码适用于任何 python 嵌套字典,即使内部字典使用与外部字典相同的键。您是否有一个可以破坏此代码的嵌套字典示例? (3认同)
  • @dreftymac我会为键添加一个访问集来避免循环:`def myprint(d):stack = d.items()visited = set()而stack:k,v = stack.pop()if isinstance(v ,dict):如果k没有被访问:stack.extend(v.iteritems())else:print("%s:%s"%(k,v))visited.add(k)` (2认同)

Fre*_*Foo 23

替代迭代解决方案:

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))
Run Code Online (Sandbox Code Playgroud)

  • 是的,这就是我想象的样子。谢谢。那么这样做的好处是它不会因为非常深的嵌套而使堆栈溢出?或者还有什么其他原因吗? (2认同)

Gab*_*iel 6

使用基于 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)
Run Code Online (Sandbox Code Playgroud)


Ehs*_*Kia 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)))
Run Code Online (Sandbox Code Playgroud)

在您的数据上,它将打印出来

data['xml']['config']['portstatus']['status'] = u'good'
data['xml']['config']['target'] = u'1'
data['xml']['port'] = u'11'
Run Code Online (Sandbox Code Playgroud)

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


Dmi*_*rba 5

这是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)
Run Code Online (Sandbox Code Playgroud)

版画

a {1: {1: 2, 3: 4}, 2: {5: 6}}
1 {1: 2, 3: 4}
1 2
3 4
2 {5: 6}
5 6
Run Code Online (Sandbox Code Playgroud)