我正在分享一个 working 函数,它能够从字典中返回一个值,而不管嵌套对象的深度。 例如: my_dict = {"People": [{"name": "Alice"}, {"name": "Bob"}]} 如果我想获取“People”id 的值,请写: my_dict["People"] (路径长度为 1) 并获取“Bob”:my_dict[“People”][1](路径长度为2) **注意检索字典中的值与列表中的值之间的区别。
当您事先不知道值的深度(路径长度)时,就会出现问题。 我已经搜索了一个可以执行此操作的函数,但没有找到。 我发布此内容是为了向其他试图实现此目标但遇到麻烦的人分享该功能。
以下函数解决了这个问题: 输入:
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"
** 请注意,此代码没有数据验证和错误处理
首先,你的
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')