Python 函数获取深度嵌套字典中的值(str 或对象)

问题描述 投票:0回答:1

我正在分享一个 working 函数,它能够从字典中返回一个值,而不管嵌套对象的深度。 例如: my_dict = {"People": [{"name": "Alice"}, {"name": "Bob"}]} 如果我想获取“People”id 的值,请写: my_dict["People"] (路径长度为 1) 并获取“Bob”:my_dict[“People”][1](路径长度为2) **注意检索字典中的值与列表中的值之间的区别。

当您事先不知道值的深度(路径长度)时,就会出现问题。 我已经搜索了一个可以执行此操作的函数,但没有找到。 我发布此内容是为了向其他试图实现此目标但遇到麻烦的人分享该功能。

以下函数解决了这个问题: 输入:

  • data:要搜索的字典
  • path:列表格式的对象路径(例如 Bob 的路径将是 ["People", 1] **这样深度并不重要,可以在运行时确定。
def get_nested_object(data: dict, path: list):
    # Get outermost key from path
    key = path[0]
    # if data is type dictionary
    if isinstance(data, dict):
        # if key is found
        if key in data:
            # if path length == 1 -> this is the object we need to return
            if len(path) == 1:
                return {key: data[key]}
            # else try to find value in current key
            else:
                return get_nested_object(data[key], path[1:])
        # else if data is type list
    elif isinstance(data, list):
        # if path length == 1 -> this is the object we need to return
        if len(path) == 1:
            return data[key]
        else:
            return get_nested_object(data[key], path[1:])
    else:
        return "Invalid data"

** 请注意,此代码没有数据验证和错误处理

python-3.x dictionary nested key-value
1个回答
0
投票

首先,你的

get_nested_object
函数过于复杂,所以我提供一个更简单的版本,没有任何错误保护。接下来是函数
find_all
,给定一个目标值,它会找到所有的(路径,值)。
find_first
函数构建在
find_all
之上。最后,
find_all
建立在
iter_values
之上,它给定一个嵌套数据结构,生成所有路径和数据。

import json


def get_nested_object(data, path):
    """Give a path, return the value"""
    for key in path:
        data = data[key]
    return data


def iter_values(data, path=None):
    """Returns all paths, values"""
    path = path or []
    if isinstance(data, (int, str, float, bool)):
        yield path, data
    elif isinstance(data, (dict)):
        for key, value in data.items():
            yield from iter_values(value, path + [key])
    elif isinstance(data, list):
        for key, value in enumerate(data):
            yield from iter_values(value, path + [key])
    else:
        raise TypeError(f"Cannot handle object {data}")


def find_all(data, target):
    """Given a target, find all paths"""
    for path, value in iter_values(data):
        if value == target:
            yield path, value


def find_first(data, target, default=None):
    """Given a target, first find path"""
    found = next(find_all(data, target), None)
    if found is None:
        return [], default
    return found


def main():
    """Entry"""
    data = {
        "People": [{"name": "Alice"}, {"name": "Bob"}],
        "Animal": {
            "Cats": [
                {"name": "Tristan"},
                {"name": "Bob"},
                {"name": "Alice"},
            ],
            "Dogs": [
                {"name": "Bob"},
                {"name": "Scooby"},
            ],
        },
    }
    print("\n# Data:")
    print(json.dumps(data, indent=4))

    print("\n# get_nested_object:")
    path = ["Animal", "Dogs", 1, "name"]
    value = get_nested_object(data, path)
    print(f"get_nested_object(data, {path}) -> {value!r}")

    print("\n# iter_values:")
    for path, value in iter_values(data):
        print(f"{path=}, {value=}")

    print("\n# find_all:")
    for path, value in find_all(data, "Bob"):
        print(f"{path=}, {value=}")

    print("\n# find_first:")
    print(f"Find first Bob: {find_first(data, 'Bob')}")
    print(f"Find first Anna: {find_first(data, 'Anna', 'Anna not found')}")


if __name__ == "__main__":
    main()

输出:

# Data:
{
    "People": [
        {
            "name": "Alice"
        },
        {
            "name": "Bob"
        }
    ],
    "Animal": {
        "Cats": [
            {
                "name": "Tristan"
            },
            {
                "name": "Bob"
            },
            {
                "name": "Alice"
            }
        ],
        "Dogs": [
            {
                "name": "Bob"
            },
            {
                "name": "Scooby"
            }
        ]
    }
}

# get_nested_object:
get_nested_object(data, ['Animal', 'Dogs', 1, 'name']) -> 'Scooby'

# iter_values:
path=['People', 0, 'name'], value='Alice'
path=['People', 1, 'name'], value='Bob'
path=['Animal', 'Cats', 0, 'name'], value='Tristan'
path=['Animal', 'Cats', 1, 'name'], value='Bob'
path=['Animal', 'Cats', 2, 'name'], value='Alice'
path=['Animal', 'Dogs', 0, 'name'], value='Bob'
path=['Animal', 'Dogs', 1, 'name'], value='Scooby'

# find_all:
path=['People', 1, 'name'], value='Bob'
path=['Animal', 'Cats', 1, 'name'], value='Bob'
path=['Animal', 'Dogs', 0, 'name'], value='Bob'

# find_first:
Find first Bob: (['People', 1, 'name'], 'Bob')
Find first Anna: ([], 'Anna not found')
© www.soinside.com 2019 - 2024. All rights reserved.