高效的 Python 函数,无需外部库即可获取嵌套字典中特定键的值,也不知道字典中键的具体静态路径

efficient python function to get value of specific key in nested dict without an external lib and without knowing concret static path to key in dict

提问人:DevArchitectMaster 提问时间:6/20/2023 最后编辑:DevArchitectMaster 更新时间:6/21/2023 访问量:120

问:

初始情况如下:

我正在寻找一个自定义函数,该函数将从嵌套字典中提取相应的值,并在没有外部库的情况下返回它,并且不会将整个静态路径弯曲到相应的键。函数“搜索路径”(dict 键)应该类似于 CSS 或 XPATH 选择器,例如

getValue(nestedDict, "[subkey1][subkey42InSubkey1]") # "[subkey1][subkey42InSubkey1]" = "search path"

此函数 () 应在 中搜索键,并在其中搜索键,然后返回值(如果找到)或 None。getValue()nestedDictsubkey1subkey42InSubkey1

但是,该函数应该是动态的,嵌套字典的深度无关紧要。 此外,“搜索路径”应该是相对指定的,即不必知道整个嵌套字典的绝对路径。

问题:

你能帮我创建这样的函数吗?
这样的函数是否应该通过递归来解决,以便比循环更有效?

非常感谢您的帮助!

Python 代码

    test_dict = {
        "a" : "1",
        "b" : {
            "1" : 2,
            "2" : 4711,
            "3" : {
                "b31" : 31
            },
            "4" : 4
        },
        "c" : "3",
        "d" : {
            "1" : 5,
            "2" : 9,
            "3" : {
                "c31" : 55
            }
        }
    }
    test_result = 55

    # get value in nested dict like CSS- respectively XPATH Selector
    def getValue(nestedDict, key):
        #TODO
        result = None
        return result 
    
####################################################################################
    
    if __name__ == '__main__':
        result = getValue(test_dict, "[3][c31]") # should return 55 as a result
        # the following call should yield the same result!
        result2 = getValue(test_dict, "[d][3][c31]") # should return 55 as a result too
        assert result == test_result
        print(result)

我有一个“非干净代码”的解决方案,我对自己不满意,所以我避免在这里发布它,以免在无意中回答问题时产生偏见。感谢您的理解!

python-3.x 字典 嵌套 循环

评论

0赞 Codist 6/20/2023
您的解决方案将需要递归以允许未知的字典深度。您还需要定义如何指定搜索条件的“规则”——例如,它是否始终采用“[X][Y]”的形式,或者是否可以有多个级别的括号,如果是这样,这对您的示例意味着什么?
0赞 Matthias 6/20/2023
函数不应该用吗?或者这正是问题所在吗?getValue(test_dicct, "[d][3][c31]")
0赞 user2390182 6/20/2023
如果有多个匹配项怎么办?这不应该返回一个值列表吗?
0赞 DevArchitectMaster 6/20/2023
@DarkKnight 我是否正确理解了您的评论,搜索路径的规则是否总是用方形“列表”括号定义?是的,所以这个想法是,一个任意深度的字典可以用作第一个参数,第二个“搜索”参数也应该是动态的,因为语法总是对应于方括号,但数字(=深度)仍然是可变的。这能回答问题吗?因此,另一个允许的调用也是“[x][y][z][zz]”(字典的最小深度必须为 4),但这在语法上不正确:“[X]y[z]”。
0赞 DevArchitectMaster 6/20/2023
@Matthias您建议的调用在语义上和语法上也是正确的,但需要我知道“键”的完整路径。这正是我的问题。

答:

2赞 user2390182 6/20/2023 #1

一种可能的方法:

递归生成器,用于在嵌套字典中的任何位置查找单个键的所有值:

def find(nested_dict, key):
    if key in nested_dict:
        yield nested_dict[key]
    for v in nested_dict.values():
        if isinstance(v, dict):
            yield from find(v, key)

find(test_dict, "3")
#   {"b31" : 31}
#   {"c31" : 55} 

帮助程序访问字典中键的具体路径:

def access(obj, bits):
    for bit in bits:
        obj = obj[bit]  # this can cause errors: obj not a dict or key missing
    return obj
 
access(test_dict, ["d", "3", "c31"])
# 55
access(test_dict, ["b", "4"])
# 4

最终值收集:

def get_values(nested_dict, search_path):  # e.g. search_path "[d][3][c31]"
    start, *tail = search_path.strip("[]").split("][")
    # start: "d"
    # tail: ["3", "c31"]
    for d in find(nested_dict, start):  # e.g. all occurrences of "d"
        try:
            yield access(d, tail)  # then access e.g. ["3", "c31"] inside of them
        except (KeyError, TypeError):
            pass  # key missing, d or any value down the path not a dict

>>> list(get_values(test_dict, "[d][3][c31]"))
[55]
>>> list(get_values(test_dict, "[3][c31]"))
[55]
>>> list(get_values(test_dict, "[c31]"))
[55]
>>> list(get_values(test_dict, "[2]"))
[4711, 9]
>>> list(get_values(test_dict, "[b][2]"))
[4711]

这将返回一个列表,因为可能有 1 个以上的匹配项。它可以很容易地修改为通过在函数中更改为仅返回第一个。yieldreturnget_values

评论

0赞 DevArchitectMaster 6/21/2023
感谢您提出的解决方案。在使用您的代码之前,我试图理解它,但不幸的是,我没有将这三个函数与生成器机制(“产量”)相结合,从而理解其背后的概念。您能否简要解释一下它是如何工作的以及您对解决方案背后的想法。谢谢!
0赞 user2390182 6/21/2023
我确实得到了相同的结果:我稍后会写一些解释。list(get_values(test_dict, "[d][3][c31]")) -> [55]
0赞 DevArchitectMaster 6/23/2023
感谢您对您提交的解决方案的解释,我现在明白了一切,我的需求得到了解决。
1赞 Viper 6/20/2023 #2

如果您将“”添加到类似 XPATH 的选择器中,这将起作用。我想你应该这样做,因为字典键不是必需的字符串。

def get_value(nested_dict, key):
    res = None
    try:
        res = eval("nested_dict" + key)
    except KeyError:
        for v in nested_dict.values():
            if isinstance(v, dict):
                res = get_value(v, key)
    return res

get_value(test_dict, "['3']['c31']")在示例中返回“55”。
请注意,如果您当时无法完全控制您的数据,那么使用是一个坏主意。
eval

评论

0赞 DevArchitectMaster 6/21/2023
非常感谢您的回答和您提出的观点。它帮助我从不同的角度看待挑战。