Python 在字典中递归查找键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14962485/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 12:58:26  来源:igfitidea点击:

Finding a key recursively in a dictionary

pythonsearchrecursiondictionary

提问by Fredrick Brennan

I'm trying to write a very simple function to recursively search through a possibly nested (in the most extreme cases ten levels deep) Python dictionary and return the first value it finds from the given key.

我正在尝试编写一个非常简单的函数来递归搜索可能嵌套的(在最极端的情况下为 10 级深)Python 字典,并返回它从给定键中找到的第一个值。

I cannot understand why my code doesn't work for nested dictionaries.

我不明白为什么我的代码不适用于嵌套字典。

def _finditem(obj, key):
    if key in obj: return obj[key]
    for k, v in obj.items():
        if isinstance(v,dict):
            _finditem(v, key)

print _finditem({"B":{"A":2}},"A")

It returns None.

它返回None

It does work, however, for _finditem({"B":1,"A":2},"A"), returning 2.

然而,它确实有效,因为_finditem({"B":1,"A":2},"A"),返回2

I'm sure it's a simple mistake but I cannot find it. I feel like there already might be something for this in the standard library or collections, but I can't find that either.

我确定这是一个简单的错误,但我找不到它。我觉得在标准库或 中可能已经有一些东西collections,但我也找不到。

采纳答案by mgilson

when you recurse, you need to returnthe result of _finditem

当你递归时,你需要return的结果_finditem

def _finditem(obj, key):
    if key in obj: return obj[key]
    for k, v in obj.items():
        if isinstance(v,dict):
            return _finditem(v, key)  #added return statement

To fix the actual algorithm, you need to realize that _finditemreturns Noneif it didn't find anything, so you need to check that explicitly to prevent an early return:

要修复实际算法,您需要意识到如果未找到任何内容则_finditem返回None,因此您需要明确检查以防止提前返回:

def _finditem(obj, key):
    if key in obj: return obj[key]
    for k, v in obj.items():
        if isinstance(v,dict):
            item = _finditem(v, key)
            if item is not None:
                return item

Of course, that will fail if you have Nonevalues in any of your dictionaries. In that case, you could set up a sentinel object()for this function and return that in the case that you don't find anything -- Then you can check against the sentinelto know if you found something or not.

当然,如果您None在任何字典中都有值,那将失败。在这种情况下,您可以object()为此函数设置一个哨兵,并在没有找到任何东西的情况下返回它——然后您可以检查sentinel以了解是否找到了某些东西。

回答by Becca Petrin

Here's a function that searches a dictionary that contains both nested dictionaries and lists. It creates a list of the values of the results.

这是一个搜索包含嵌套字典和列表的字典的函数。它创建一个结果值列表。

def get_recursively(search_dict, field):
    """
    Takes a dict with nested lists and dicts,
    and searches all dicts for a key of the field
    provided.
    """
    fields_found = []

    for key, value in search_dict.iteritems():

        if key == field:
            fields_found.append(value)

        elif isinstance(value, dict):
            results = get_recursively(value, field)
            for result in results:
                fields_found.append(result)

        elif isinstance(value, list):
            for item in value:
                if isinstance(item, dict):
                    more_results = get_recursively(item, field)
                    for another_result in more_results:
                        fields_found.append(another_result)

    return fields_found

回答by alecxe

Here is a way to do this using a "stack" and the "stack of iterators" pattern(credits to Gareth Rees):

这是一种使用“堆栈”和“迭代器堆栈”模式(归功于 Gareth Rees)的方法:

def search(d, key, default=None):
    """Return a value corresponding to the specified key in the (possibly
    nested) dictionary d. If there is no item with that key, return
    default.
    """
    stack = [iter(d.items())]
    while stack:
        for k, v in stack[-1]:
            if isinstance(v, dict):
                stack.append(iter(v.items()))
                break
            elif k == key:
                return v
        else:
            stack.pop()
    return default

The print(search({"B": {"A": 2}}, "A"))would print 2.

print(search({"B": {"A": 2}}, "A"))将打印2

回答by Pablo

I couldn't add a comment to the accepted solution proposed by @mgilston because of lack of reputation. The solution doesn't work if the key being searched for is inside a list.

由于缺乏声誉,我无法对@mgilston 提出的已接受解决方案添加评论。如果要搜索的键在列表中,则该解决方案不起作用。

Looping through the elements of the lists and calling the recursive function should extend the functionality to find elements inside nested lists:

循环遍历列表的元素并调用递归函数应该扩展功能以查找嵌套列表中的元素:

def _finditem(obj, key):
    if key in obj: return obj[key]
    for k, v in obj.items():
        if isinstance(v,dict):
            item = _finditem(v, key)
            if item is not None:
                return item
        elif isinstance(v,list):
            for list_item in v:
                item = _finditem(list_item, key)
                if item is not None:
                    return item

print(_finditem({"C": {"B": [{"A":2}]}}, "A"))

回答by isometric

I had to create a general-case version that finds a uniquely-specified key (a minimal dictionary that specifies the path to the desired value) in a dictionary that contains multiple nested dictionaries and lists.

我必须创建一个通用版本,它可以在包含多个嵌套字典和列表的字典中找到唯一指定的键(一个指定所需值路径的最小字典)。

For the example below, a target dictionary is created to search, and the key is created with the wildcard "???". When run, it returns the value "D"

对于下面的示例,创建了一个目标字典进行搜索,并使用通配符“???”创建键。运行时,它返回值“D”

def lfind(query_list:List, target_list:List, targ_str:str = "???"):
    for tval in target_list:
        #print("lfind: tval = {}, query_list[0] = {}".format(tval, query_list[0]))
        if isinstance(tval, dict):
            val = dfind(query_list[0], tval, targ_str)
            if val:
                return val
        elif tval == query_list[0]:
            return tval

def dfind(query_dict:Dict, target_dict:Dict, targ_str:str = "???"):
    for key, qval in query_dict.items():
        tval = target_dict[key]
        #print("dfind: key = {}, qval = {}, tval = {}".format(key, qval, tval))
        if isinstance(qval, dict):
            val =  dfind(qval, tval, targ_str)
            if val:
                return val
        elif isinstance(qval, list):
            return lfind(qval, tval, targ_str)
        else:
            if qval == targ_str:
                return tval
            if qval != tval:
                break

def find(target_dict:Dict, query_dict:Dict):
    result = dfind(query_dict, target_dict)
    return result



target_dict = {"A":[
    {"key1":"A", "key2":{"key3": "B"}},
    {"key1":"C", "key2":{"key3": "D"}}]
}
query_dict = {"A":[{"key1":"C", "key2":{"key3": "???"}}]}

result = find(target_dict, query_dict)
print("result = {}".format(result))

回答by Элина Ахманова

Just trying to make it shorter:

只是试图让它更短:

def get_recursively(search_dict, field):
    if isinstance(search_dict, dict):
        if field in search_dict:
            return search_dict[field]
        for key in search_dict:
            item = get_recursively(search_dict[key], field)
            if item is not None:
                return item
    elif isinstance(search_dict, list):
        for element in search_dict:
            item = get_recursively(element, field)
            if item is not None:
                return item
    return None