在嵌套的python字典和列表中查找所有出现的键

我有这样一本字典:

{ "id" : "abcde", "key1" : "blah", "key2" : "blah blah", "nestedlist" : [ { "id" : "qwerty", "nestednestedlist" : [ { "id" : "xyz", "keyA" : "blah blah blah" }, { "id" : "fghi", "keyZ" : "blah blah blah" }], "anothernestednestedlist" : [ { "id" : "asdf", "keyQ" : "blah blah" }, { "id" : "yuiop", "keyW" : "blah" }] } ] } 

基本上是一个任意深度的嵌套列表,字典和string的字典。

遍历这个提取每个“id”键值的最好方法是什么? 我想实现类似于“// id”的XPath查询。 “id”的值总是一个string。

所以从我的例子来说,我需要的输出基本上是:

 ["abcde", "qwerty", "xyz", "fghi", "asdf", "yuiop"] 

订单不重要。

 d = { "id" : "abcde", "key1" : "blah", "key2" : "blah blah", "nestedlist" : [ { "id" : "qwerty", "nestednestedlist" : [ { "id" : "xyz", "keyA" : "blah blah blah" }, { "id" : "fghi", "keyZ" : "blah blah blah" }], "anothernestednestedlist" : [ { "id" : "asdf", "keyQ" : "blah blah" }, { "id" : "yuiop", "keyW" : "blah" }] } ] } def fun(d): if 'id' in d: yield d['id'] for k in d: if isinstance(d[k], list): for i in d[k]: for j in fun(i): yield j 

 >>> list(fun(d)) ['abcde', 'qwerty', 'xyz', 'fghi', 'asdf', 'yuiop'] 

我发现这个Q / A非常有趣,因为它为同样的问题提供了几种不同的解决scheme。 我把所有这些函数都用一个复杂的字典对象来testing。 我不得不从testing中拿出两个function,因为他们有很多失败的结果,他们不支持返回列表或字符作为值,我认为这是必不可less的,因为一个函数应该为几乎所有的数据准备好。

于是我通过timeit模块在100.000次迭代中抽取了其他函数,输出结果如下:

 0.11 usec/pass on gen_dict_extract(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6.03 usec/pass on find_all_items(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.15 usec/pass on findkeys(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.79 usec/pass on get_recursively(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.14 usec/pass on find(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.36 usec/pass on dict_extract(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

所有函数都有相同的针来search(“日志logging”)和相同的字典对象,它是这样构造的:

 o = { 'temparature': '50', 'logging': { 'handlers': { 'console': { 'formatter': 'simple', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', 'level': 'DEBUG' } }, 'loggers': { 'simpleExample': { 'handlers': ['console'], 'propagate': 'no', 'level': 'INFO' }, 'root': { 'handlers': ['console'], 'level': 'DEBUG' } }, 'version': '1', 'formatters': { 'simple': { 'datefmt': "'%Y-%m-%d %H:%M:%S'", 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s' } } }, 'treatment': {'second': 5, 'last': 4, 'first': 4}, 'treatment_plan': [[4, 5, 4], [4, 5, 4], [5, 5, 5]] } 

所有的function都提供了相同的结果,但时间差异很大! 函数gen_dict_extract(k,o)是我在这里调用函数的函数,实际上它和Alfe的find函数非常相似,主要区别在于,我检查给定对象是否具有iteritems函数,如果string是在recursion过程中通过:

 def gen_dict_extract(key, var): if hasattr(var,'iteritems'): for k, v in var.iteritems(): if k == key: yield v if isinstance(v, dict): for result in gen_dict_extract(key, v): yield result elif isinstance(v, list): for d in v: for result in gen_dict_extract(key, d): yield result 

所以这个变种是这里最快和最安全的function。 而find_all_items速度非常慢,远离第二个最慢的get_recursivley而其余的,除了dict_extract ,是彼此接近。 fun的function和keyHole只有在你正在寻找string时才有效。

这里有趣的学习方面:)

 def find(key, value): for k, v in value.iteritems(): if k == key: yield v elif isinstance(v, dict): for result in find(key, v): yield result elif isinstance(v, list): for d in v: for result in find(key, d): yield result 
 d = { "id" : "abcde", "key1" : "blah", "key2" : "blah blah", "nestedlist" : [ { "id" : "qwerty", "nestednestedlist" : [ { "id" : "xyz", "keyA" : "blah blah blah" }, { "id" : "fghi", "keyZ" : "blah blah blah" }], "anothernestednestedlist" : [ { "id" : "asdf", "keyQ" : "blah blah" }, { "id" : "yuiop", "keyW" : "blah" }] } ] } def findkeys(node, kv): if isinstance(node, list): for i in node: for x in findkeys(i, kv): yield x elif isinstance(node, dict): if kv in node: yield node[kv] for j in node.values(): for x in findkeys(j, kv): yield x print list(findkeys(d, 'id')) 

这是我做的。

此函数recursionsearch包含嵌套字典和列表的字典。 它build立一个名为fields_found的列表,其中包含每次find该字段的值。 “字段”是我在字典和它的嵌套列表和字典中寻找的关键。

 def get_recursively(search_dict,field):
     “”“用一个嵌套的列表和字典,
    并search所有的字典的字段的关键
    提供。
     “””
     fields_found = []

     for search_dict.iteritems()中的键值:

        如果键==字段:
             fields_found.append(值)

         elif isinstance(value,dict):
             results = get_recursively(value,field)
            为了结果:
                 fields_found.append(结果)

         elif isinstance(value,list):
            物品价值:
                如果isinstance(item,dict):
                     more_results = get_recursively(item,field)
                    为more_results中的another_result:
                         fields_found.append(another_result)

    返回fields_found

另一个变体,其中包括find结果的嵌套path( 注意:此版本不考虑列表 ):

 def find_all_items(obj, key, keys=None): """ Example of use: d = {'a': 1, 'b': 2, 'c': {'a': 3, 'd': 4, 'e': {'a': 9, 'b': 3}, 'j': {'c': 4}}} for k, v in find_all_items(d, 'a'): print "* {} = {} *".format('->'.join(k), v) """ ret = [] if not keys: keys = [] if key in obj: out_keys = keys + [key] ret.append((out_keys, obj[key])) for k, v in obj.items(): if isinstance(v, dict): found_items = find_all_items(v, key, keys=(keys+[k])) ret += found_items return ret 

这是我的刺伤:

 def keyHole(k2b,o): # print "Checking for %s in "%k2b,o if isinstance(o, dict): for k, v in o.iteritems(): if k == k2b and not hasattr(v, '__iter__'): yield v else: for r in keyHole(k2b,v): yield r elif hasattr(o, '__iter__'): for r in [ keyHole(k2b,i) for i in o ]: for r2 in r: yield r2 return 

例:

 >>> findMe = {'Me':{'a':2,'Me':'bop'},'z':{'Me':4}} >>> keyHole('Me',findMe) <generator object keyHole at 0x105eccb90> >>> [ x for x in keyHole('Me',findMe) ] ['bop', 4]