遍历嵌套字典

20
有没有一种简单的方法可以迭代嵌套字典,其中可能包含其他对象,如列表、元组,然后再次包含字典,以便迭代覆盖这些其他对象的所有元素?
例如,如果我键入嵌套字典对象的键,我希望在Python解释器中将其全部列出。

[编辑] 这是示例字典:

{
'key_1': 'value_1',
'key_2': {'key_21': [(2100, 2101), (2110, 2111)],
      'key_22': ['l1', 'l2'],
      'key_23': {'key_231': 'v'},
      'key_24': {'key_241': 502,
             'key_242': [(5, 0), (7, 0)],
             'key_243': {'key_2431': [0, 0],
                 'key_2432': 504,
                 'key_2433': [(11451, 0), (11452, 0)]
                },
             'key_244': {'key_2441': {'key_24411': {'key_244111': 'v_24411',
                                'key_244112': [(5549, 0)]
                               },
                          'key_24412':'v_24412'
                         },
                 'key_2441': ['ll1', 'll2']
                }
            },
     }
}

很抱歉这段内容可能难以理解,但我已经尽了最大的努力。


对于你问题的第二部分,你可能需要一个漂亮的打印机(pretty printer)。请参考:http://www.doughellmann.com/PyMOTW/pprint/。 - miku
2
为什么要贬低而不给出理由?问题问得不好吗?字典对象示例不好吗?这只是一个更大的字典的一部分,如果可能的话,我想以自动化的方式使用graphviz,而不需要手动爬取。 - theta
2
有些人一看到别人试图遍历字典或哈希表,就很着急。 - prusswan
为什么没有被接受的答案?Graddy和@NeilenMarais的解决方案都很好。 - zelusp
1
@zelusp:http://code.activestate.com/recipes/577982-recursively-walk-python-objects/ - theta
@theta,你是怎么让代码返回一个键的元组和值的列表而不是一个键的列表以及每个值分别的? - FancyDolphin
6个回答

19
def recurse(d):
  if type(d)==type({}):
    for k in d:
      recurse(d[k])
  else:
    print d

2
@theta:对于你问题的一个解释,它可以正常工作。如果你想让它也循环遍历列表,请添加 elif isinstance([], (list, tuple)): 然后 for v in d: recurse(v) - naught101
1
注意:在大型嵌套字典的情况下,递归方法会失败...会出现错误:超过最大递归深度。因此,这取决于您的需求。在处理大型嵌套字典问题时,请使用迭代方法。 - MANU

7
Graddy的上述recurse()答案的生成器版本,不会在字符串上出错,并且会给你提供复合键(饼干屑路径?),显示你如何得到某个值:
def recurse(d, keys=()):
    if type(d) == dict:
         for k in d:
            for rv in recurse(d[k], keys + (k, )):
                yield rv
    else:
        yield (keys, d)

for compound_key, val in recurse(eg_dict):
    print '{}: {}'.format(compound_key, val)

产生输出(使用问题中提供的示例字典):
('key_1',): value_1
('key_2', 'key_21'): [(2100, 2101), (2110, 2111)]
('key_2', 'key_22'): ['l1', 'l2']
('key_2', 'key_23', 'key_231'): v
('key_2', 'key_24', 'key_241'): 502
('key_2', 'key_24', 'key_243', 'key_2433'): [(11451, 0), (11452, 0)]
('key_2', 'key_24', 'key_243', 'key_2432'): 504
('key_2', 'key_24', 'key_243', 'key_2431'): [0, 0]
('key_2', 'key_24', 'key_242'): [(5, 0), (7, 0)]
('key_2', 'key_24', 'key_244', 'key_2441'): ['ll1', 'll2']

在 Python 3 中,第二个 yield 循环应该可以用 yield from 来代替。通过使用来自 collections 模块的 Mapping ABC,可以将此生成器更加通用,将 type(d) == dict 的测试替换为 isinstance(d, collections.Mapping)

3

这里是另一种解决方案,

#!/usr/bin/python

d = {'key_1': 'value_1',
     'key_2': {'key_21': [(2100, 2101), (2110, 2111)],
           'key_22': ['l1', 'l2'],
           'key_23': {'key_231': 'v'},
           'key_24': {'key_241': 502,
                      'key_242': [(5, 0), (7, 0)],
                      'key_243': {'key_2431': [0, 0],
                                  'key_2432': 504,
                                  'key_2433': [(11451, 0), (11452, 0)]},
                      'key_244': {'key_2441': ['ll1', 'll2']}}}}

def search_it(nested, target):
    found = []
    for key, value in nested.iteritems():
        if key == target:
            found.append(value)
        elif isinstance(value, dict):
            found.extend(search_it(value, target))
        elif isinstance(value, list):
            for item in value:
                if isinstance(item, dict):
                    found.extend(search_it(item, target))
        else:
            if key == target:
                found.append(value)
    return found

keys = [ 'key_242', 'key_243', 'key_242', 'key_244', 'key_1' ]

for key in keys:
    f = search_it(d, key)
    print 'Key: %s, value: %s' % (key, f[0])

输出:

Key: key_242, value: [(5, 0), (7, 0)]
Key: key_243, value: {'key_2433': [(11451, 0), (11452, 0)], 'key_2432': 504, 'key_2431': 
 [0, 0]}
Key: key_242, value: [(5, 0), (7, 0)]
Key: key_244, value: {'key_2441': ['ll1', 'll2']}
Key: key_1, value: value_1

2

遍历包含意外嵌套元素的嵌套字典。

这是我的解决方案:

# d is the nested dictionary

for item in d:
    if type(item) == list:
        print "Item is a list"
        for i in item: print i
    elif type(item) == dict:
        print "Item is a dict"
        for i in item: print i
    elif type(item) == tuple:
        print "Item is a tuple"
        for i in item: print i
    else:
        print "Item is not a list, neither a dict and not even a tuple"
        print item

我认为以上示例是非常通用的,你可以根据你的用例进行调整。


0

我提取了你的字典中的一小部分

d = {
    'key_1': 'value_1',
    'key_2': {'key_21': [(2100, 2101), (2110, 2111)]},
    'key_22': ['l1', 'l2'],
    'key_23': {'key_231': 'v'}
}

使用 NestedDict 可以轻松访问所有值。

>>> from ndicts.ndicts import NestedDict
>>> nd = NestedDict(d)
>>> list(nd.values())
['value_1', [(2100, 2101), (2110, 2111)], ['l1', 'l2'], 'v']

如果您想要将此列表展平,可以使用这个递归函数。
from typing import Iterable

def flatten(li):
    def wrap(x, result):
        if isinstance(x, Iterable) and type(x) is not str:
            for i in x:
                wrap(i, result)
        else:
            result.append(x)
    result = []
    wrap(li, result)
    return result

>>> flatten(list(nd.values()))
['value_1', 2100, 2101, 2110, 2111, 'l1', 'l2', 'v']

0

使用通用的包装器生成器,例如以下内容,怎么样?

def recursive(coll):
    """Return a generator for all atomic values in coll and its subcollections.
    An atomic value is one that's not iterable as determined by iter."""
    try:
        k = iter(coll)
        for x in k:
            for y in recursive(x):
                yield y
    except TypeError:
        yield coll


def test():
    t = [[1,2,3], 4, 5, [6, [7, 8], 9]]
    for x in recursive(t):
        print x

2
我得到了:RuntimeError: 最大递归深度超过限制 - theta
2
字符串是可迭代的,因此如果列表/字典中有任何字符串,它将会崩溃。 - UsAaR33

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接