python:一种在嵌套字典中获取详尽,有序的键列表的方法吗?

时间:2010-05-08 01:55:42

标签: python dictionary

详尽
- 字典中的所有键,即使键位于嵌套字典中,也是上一级字典键的值。

排序
- 这是为了确保按键始终以相同的顺序返回

嵌套是任意深的。首选非递归算法。

level1 = {
    'a'         : 'aaaa',
    'level2_1'  : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd'}  },
    'level2_2'  : { 'z': 'zzzzzzz' }
}

注意:字典值可以包括列表(可以将字典作为元素),例如

tricky = {'category': [{'content': 'aaaaa'}, {'content': 'bbbbbb'}]}

5 个答案:

答案 0 :(得分:6)

def _auxallkeys(aset, adict):
  aset.update(adict)
  for d in adict.itervalues():
    if isinstance(d, dict):
       _auxallkeys(aset, d)

def allkeys(adict):
  aset = set()
  _auxallkeys(aset, adict)
  return sorted(aset)

是明显的(递归)解决方案。消除递归:

def allkeys(adict):
  aset = set()
  pending = [adict]
  while pending:
    d = pending.pop()
    aset.update(d)
    for dd in d.itervalues():
      if isinstance(dd, dict):
         pending.append(dd)
  return sorted(aset)

因为处理各种嵌套dicts的 order 对此无关紧要。

编辑:OP评论抱怨说,如果dict没有嵌套,它就不起作用,而是在列表中(我回答它也可能是在一个元组中,一个具有每个实例或每个类的属性的对象[可能是它的基类],一个架子,以及许多其他隐藏房子周围的方法的方法;-)。如果OP将设法通过“嵌套”准确定义他的意思(显然与普通凡人适用于所讨论的词的意义不同),可能更容易帮助他。同时,这是一个涵盖列表(和元组,但不是生成器,许多itertools类的实例,架子等等)的版本;

def allkeys(adict):
  aset = set()
  pending = [adict]
  pendlis = []

  def do_seq(seq):
    for dd in seq:
      if isinstance(dd, dict):
        pending.append(dd)
      elif isinstance(dd, (list, tuple)):
        pendlis.append(dd)

  while pending or pendlis:
    while pending:
      d = pending.pop()
      aset.update(d)
      do_seq(d.itervalues())
    while pendlis:
      l = pendlis.pop()
      do_seq(l)

  return sorted(aset)

答案 1 :(得分:1)

非递归方法现在对我来说并不明显。以下适用于您的原始示例。编辑:它现在将处理dict中列表中的dicts,至少是Alex Martelli回答评论中引用的棘手例子中的一个。

#!/usr/bin/env python
import types

def get_key_list(the_dict, key_list):
    for k, v in (the_dict.iteritems()):
        key_list.append(k)
        if type(v) is types.DictType:
            get_key_list(v, key_list)
        if type(v) is types.ListType:
            for lv in v:
                if type(lv) is types.DictType:
                    get_key_list(lv, key_list)
    return

level1 = {
    'a'         : 'aaaa',
    'level2_1'  : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd'}  },
    'level2_2'  : { 'z': 'zzzzzzz' }
}
tricky = {'category': [{'content': 'aaaaa'}, {'content': 'bbbbbb'}]}

key_list = []
get_key_list(level1, key_list)
key_list.sort()
print key_list

key_list = []
get_key_list(tricky, key_list)
key_list.sort()
print key_list

输出:

  

['a','b','c','d','level2_1','level2_2','level3','z']
  ['category','content','content']

答案 2 :(得分:1)

这是一个非递归解决方案,它处理生成器以及列表,元组和dicts,如果一个键出现多次,则添加所有连续的键:

def get_iterator(i):
    if hasattr(i, 'next'):
        # already an iterator - use it as-is!
        return i
    elif hasattr(i, '__iter__') and not isinstance(i, basestring):
        # an iterable type that isn't a string
        return iter(i)
    else: 
        # Can't iterate most other types!
        return None

def get_dict_keys(D):
    LRtn = []
    L = [(D, get_iterator(D))]
    while 1:
        if not L: break
        cur, _iter = L[-1]

        if _iter:
            # Get the next item
            try: 
                i = _iter.next()
            except StopIteration:
                del L[-1]
                continue

        if isinstance(cur, dict): 
            # Process a dict and all subitems
            LRtn.append(i)

            _iter = get_iterator(cur[i])
            if _iter: L.append((cur[i], _iter))
        else:
            # Process generators, lists, tuples and all subitems
            _iter = get_iterator(i)
            if _iter: L.append((i, _iter))

    # Sort and return
    LRtn.sort()
    return LRtn

D = {
    'a'         : 'aaaa',
    'level2_1'  : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd', 'e': 134, 'f': [{'blah': 553}]}  },
    'level2_2'  : { 'z': 'zzzzzzz' },
    'blah2': iter([{'blah3': None}]),
}

print get_dict_keys(D)

编辑:稍微提高速度并缩短代码。

答案 3 :(得分:1)

我也更喜欢递归方法......

#!/usr/bin/env python

def extract_all_keys(structure):
    try:
        list_of_keys = structure.keys()
        for value in structure.values():
            add_all_keys_in_value_to_list(value, list_of_keys)
    except AttributeError:
        list_of_keys = []
    return list_of_keys.sort()


def add_all_keys_in_value_to_list(value, list_of_keys):
    if isinstance(value, dict):
        list_of_keys += extract_all_keys(value)
    elif isinstance(value, (list, tuple)):
        for element in value:
            list_of_keys += extract_all_keys(element)


import unittest

class TestKeys(unittest.TestCase):

    def given_a_structure_of(self, structure):
        self.structure = structure


    def when_keys_are_extracted(self):
        self.list_of_keys = extract_all_keys(self.structure)


    def testEmptyDict(self):
        self.given_a_structure_of({})
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, [])


    def testOneElement(self):
        self.given_a_structure_of({'a': 'aaaa'})
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, ['a'])


    def testTwoElementsSorted(self):
        self.given_a_structure_of({
            'z': 'zzzz',
            'a': 'aaaa',
            })
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, ['a', 'z'])


    def testNestedElements(self):
        self.given_a_structure_of({
            'a': {'aaaa': 'A',},
            'b': {'bbbb': 'B',},
            })
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, ['a', 'aaaa', 'b', 'bbbb'])


    def testDoublyNestedElements(self):
        self.given_a_structure_of({
            'level2': {'aaaa': 'A',
                'level3': {'bbbb': 'B'}
                }
            })
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, ['aaaa', 'bbbb', 'level2', 'level3'])


    def testNestedExampleOnStackOverflow(self):
        level1 = {
                'a'         : 'aaaa',
                'level2_1'  : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd'}  },
                'level2_2'  : { 'z': 'zzzzzzz' }
                }
        self.given_a_structure_of(level1)
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, ['a', 'b', 'c', 'd', 'level2_1', 'level2_2', 'level3', 'z'])


    def testListExampleOnStackOverflow(self):
        tricky = {'category': [{'content': 'aaaaa'}, {'content': 'bbbbbb'}]}
        self.given_a_structure_of(tricky)
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, ['category', 'content', 'content'])


    def testTuplesTreatedLikeLists(self):
        tricky_tuple = {'category': ({'content': 'aaaaa'}, {'content': 'bbbbbb'})}
        self.given_a_structure_of(tricky_tuple)
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, ['category', 'content', 'content'])


    def testCanHandleString(self):
        self.given_a_structure_of('keys')
        self.when_keys_are_extracted()
        self.assertEquals(self.list_of_keys, [])

if __name__ == '__main__':
    unittest.main()

答案 4 :(得分:0)

我认为使用递归方法更好。 我的代码如下。

level1 = {
    'a'         : 'aaaa',
    'level2_1'  : {'b': 'bbbbb', 'level3': {'c': 'cccc', 'd': 'dddddd'}  },
    'level2_2'  : { 'z': 'zzzzzzz' }
}

all_keys=[]       # a global list to store all the keys in level1

def depth ( dict ):
    for k in dict:
        if type(dict[k]) == type(dict):     #judge the type of elements in dictionary  
            depth(dict[k])                  # recursive 
        else:
            all_keys.append(k)

depth(level1)

print all_keys