我有一个动态构建的字典,看起来像这样:
my_dict = {"ministry": {
"of": {
"silly": {
"walks": {}
}
}
},
"foo": "bar",
"spam": {
"ministry": {
"of": "silly"
}
}
}
然后我有一个键列表,它是我想要找到的对象的地址,如下所示:
my_keys = ["ministry", "of", "silly", "walks"]
使用my_keys到达my_dict中的位置的最佳方法是什么。在那里,我还想添加一个新条目。
几天来一直在思考和搜索,但没有找到任何东西,只是认为我能想到的是将它重新制作为XML对象。
我想做点什么
my_dict[my_keys[0]][my_keys[1]][my_keys[2]][my_keys[3]] = {"new":"entry"}
但我永远不知道my_keys列表中有多少项。你们怎么解决这个问题?
编辑1: 基于Pauls回答我做了这个
def get(json_object, path):
if type(path) == str:
path = path.split(".")
if type(path) != list or len(path) == 0:
return
key = path.pop(0)
if len(path) == 0:
try:
return json_object[key]
except KeyError:
return
if len(path):
return get(json_object[key], path)
然后使用如下:
my_found_item = get(my_dict, my_keys)
if my_found_item:
my_found_item["new"] = "entry"
print(my_dict)
如果my_keys是这样的话,哪个有效:
my_keys = ["ministry", "of", "silly"]
但是如果my_keys像这样一直向下移动它将无法正常工作
my_keys = ["ministry", "of", "silly", "walks"]
my_found_item = get(my_dict, my_keys)
if my_found_item:
my_found_item["new"] = "entry"
有没有人知道如何解决这个问题?'小'问题
编辑2 这当然是因为我需要检查my_found_item是否不是。此更改修复了它:
if my_found_item is not None:
my_found_item["new"] = "entry"
用答案here
回答了我自己的问题答案 0 :(得分:2)
使用递归。
您想要的功能已写入名为dotty的Javascript库中。现在你不能简单地将JS复制并粘贴到python中,你需要做一些翻译。这里要注意的是技术。
特别是他们编写了一个.get()
函数,该函数将深入查看对象,以查找使用包含点的字符串定义的路径,例如ministry.of.silly.walks
实施源代码为here:
var get = module.exports.get = function get(object, path) { if (typeof path === "string") { path = path.split("."); } if (!(path instanceof Array) || path.length === 0) { return; } path = path.slice(); var key = path.shift(); if (typeof object !== "object" || object === null) { return; } if (path.length === 0) { return object[key]; } if (path.length) { return get(object[key], path); } };
path
),并确定它是什么。 ministry.of.silly.walks
在.
上拆分,以获取存储在路径中的数组(即路径现在设置为['ministry','of','silly','walks']
path
的第一个元素已移除path.shift()
并放入key
,用于访问对象的一个级别(object[key]
)。 path.length === 0
),我们就完成了并找到了元素(object[key]
)。如果没有,我们传递简化级别对象(object[key]
)和路径数组的剩余部分以递归方式返回到get(return get(object[key], path)
)。这个递归配方应该适用于任何语言。我想你会发现python有.shift()
而你.slice()
想要替换python中的复制操作,可能是path = path[:]
或path = copy.copy(path)
。如果要在找到对象中的正确位置后添加新条目,则需要为其添加参数并将其复制到递归调用。
答案 1 :(得分:1)
如果您信任输入,则可以从键构建字符串并使用exec:
my_dict = {"ministry": {
"of": {
"silly": {
"walks": {}
}
}
},
"foo": "bar",
"spam": {
"ministry": {
"of": "silly"
}
}
}
my_keys = ["ministry", "of", "silly", "walks"]
exec """my_dict{} = {{"new":"entry"}}""".format("".join(map("['{}']".format, my_keys)))
演示:
In [47]: my_dict = {"ministry": {
"of": {
"silly": {
"walks": {}
}
}
},
"foo": "bar",
"spam": {
"ministry": {
"of": "silly"
}
}
}
In [48]: my_keys = ["ministry", "of", "silly", "walks"]
In [49]: exec """my_dict{} = {{"new":"entry"}}""".format("".join(map("['{}']".format, my_keys)))
In [50]: my_dict
Out[50]:
{'foo': 'bar',
'ministry': {'of': {'silly': {'walks': {'new': 'entry'}}}},
'spam': {'ministry': {'of': 'silly'}}}
如果您还想检索值:
def pairing(d, new,keylist):
path = "".join(map("['{}']".format, keylist))
exec "get_val = d{}".format(path)
exec "d{path} = {new}".format(new=new,path=path)
return get_val
演示:
In [58]: my_dict = {"ministry": {
"of": {
"silly": {
"walks": {}
}
}
},
"foo": "bar",
"spam": {
"ministry": {
"of": "silly"
}
}
}
In [59]: pairing(my_dict, {"new":"pairing"}, my_keys)
Out[59]: {}
In [60]: my_dict
Out[60]:
{'foo': 'bar',
'ministry': {'of': {'silly': {'walks': {'new': 'pairing'}}}},
'spam': {'ministry': {'of': 'silly'}}}
如果你传递了错误的密钥,你会得到一个像你通常的keyError:
n [61]: my_keys = ["ministry", "of", "silly", "foo"]
In [62]: pairing(my_dict, {"new":"pairing"}, my_keys)
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
...................
<string> in <module>()
KeyError: 'foo'
如果您愿意,可以像往常一样使用try/except
或dict.get
来捕捉任何错误。
答案 2 :(得分:1)
基于Pauls关于名为dotty的Javascript的回答。
my_dict = {"ministry": {
"of": {
"silly": {
"walks": {}
}
}
},
"foo": "bar",
"spam": {
"ministry": {
"of": "silly"
}
}
}
my_keys = ["ministry", "of", "silly"]
def get_json(json_object, path):
__path = path[:]
if type(__path) == str:
__path = __path.split(".")
if type(path) != list or len(__path) == 0:
return
key = __path.pop(0)
if len(__path) == 0:
try:
return json_object[key]
except KeyError:
return
if len(__path):
return get_json(json_object[key], __path)
my_found_item = get(my_dict, my_keys)
if my_found_item is not None:
my_found_item["new"] = "entry"
print(my_dict)
答案 3 :(得分:0)
您可以使用jsonpointer访问字典中的元素
from jsonpointer import resolve_pointer
my_pointer = '/' + '/'.join(my_keys)
resolve_pointer(my_dict, my_pointer)['new'] = 'entry'
修改强> 我刚刚发现jsonpath.rw,看起来比jsonpointer更强大