【问题标题】:python: a way to get an exhaustive, sorted list of keys in a nested dictionary?python:一种在嵌套字典中获取详尽的排序键列表的方法?
【发布时间】:2010-05-11 20:03:30
【问题描述】:

详尽
- 字典中的所有键,即使这些键位于作为上一级字典键值的嵌套字典中。

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

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

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

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

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

【问题讨论】:

  • 到目前为止你尝试过什么?你知道如何获取字典 one 深度的排序键列表吗?

标签: python dictionary


【解决方案1】:
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)

因为处理各种嵌套字典的顺序对于这个目的并不重要。

编辑:OP cmets 抱怨如果 dict 不是嵌套的,而是在 list 中它不起作用(我回答说它也可能是在元组中,具有每个实例或每个类属性的对象[可能是其基类],一个架子,以及许多其他在房子周围隐藏字典的方法;-)。如果 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)

【讨论】:

  • 这很有效。当值是字典列表时,它会失败。 tricky = {'category': [{'content': 'aaaaa'}, {'content': 'bbbbbb'}]}
  • @saidimu,当字典被隐藏时,例如作为任意对象的属性值、元组中的项目、shelve.shelf 实例中的值等等——如果你希望任何人理解你的内容,你最好通过编辑你的问题来更准确地定义“嵌套”意思是“嵌套”(这不是这个术语的正常含义;-)。
  • 关于第一种解决方案:如果将密钥存储在一个集合中,重复的密钥只会出现一次,这可能不会。
  • @Cristian,对所有解决方案都很明显:如果您 想要 重复,这很不寻常但并非闻所未闻,显然是 s/set/list/s/update/extend - 这很简单很明显。
【解决方案2】:

目前对我来说,非递归方法并不明显。以下适用于您的原始示例。编辑:它现在将处理字典中的列表中的字典,至少是在对 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']
['类别','内容','内容']

【讨论】:

    【解决方案3】:

    这是一个非递归解决方案,它处理生成器以及列表、元组和字典,并在键出现多次时添加所有连续键:

    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)
    

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

    【讨论】:

      【解决方案4】:

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

      #!/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()
      

      【讨论】:

        【解决方案5】:

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

        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
        

        【讨论】:

        • 多么积极的可怕想法,以隐藏内置dict名称的方式命名depth的参数!
        • 这遗漏了一些键:'level2_1''level2_2'。当值是字典列表时,它也会失败,例如tricky = {'category': [{'content': 'aaaaa'}, {'content': 'bbbbbb'}]}
        • @Alex Martelli,呃......使用 dict 作为参数名称是一个可怕的想法。我知道 dict 是默认的内置名称。谢谢。
        猜你喜欢
        • 2021-05-10
        • 2012-10-25
        • 1970-01-01
        • 2018-03-17
        • 2016-12-27
        • 2019-02-08
        • 2014-11-27
        • 1970-01-01
        相关资源
        最近更新 更多