Python 是否通过键列表访问嵌套字典项?
我有一个复杂的字典结构,我想通过一个键列表来访问它,以找到正确的项Python 是否通过键列表访问嵌套字典项?,python,list,dictionary,Python,List,Dictionary,我有一个复杂的字典结构,我想通过一个键列表来访问它,以找到正确的项 dataDict={ “a”:{ “r”:1, “s”:2, “t”:3 }, “b”:{ “u”:1, “五”:{ “x”:1, “y”:2, “z”:3 }, “w”:3 } } 地图列表=[“a”,“r”] 或 maplist = ["b", "v", "y"] 我已经编写了以下代码,这些代码很有效,但我相信如果有人有想法,会有更好、更有效的方法来实现这一点 # Get a given data from a
dataDict={
“a”:{
“r”:1,
“s”:2,
“t”:3
},
“b”:{
“u”:1,
“五”:{
“x”:1,
“y”:2,
“z”:3
},
“w”:3
}
}
地图列表=[“a”,“r”]
或
maplist = ["b", "v", "y"]
我已经编写了以下代码,这些代码很有效,但我相信如果有人有想法,会有更好、更有效的方法来实现这一点
# Get a given data from a dictionary with position provided as a list
def getFromDict(dataDict, mapList):
for k in mapList: dataDict = dataDict[k]
return dataDict
# Set a given data in a dictionary with position provided as a list
def setInDict(dataDict, mapList, value):
for k in mapList[:-1]: dataDict = dataDict[k]
dataDict[mapList[-1]] = value
使用reduce()
遍历字典:
from functools import reduce # forward compatibility for Python 3
import operator
def getFromDict(dataDict, mapList):
return reduce(operator.getitem, mapList, dataDict)
并重用getFromDict
,找到存储setindicat()值的位置。
:
除了映射列表中的最后一个元素之外,所有元素都需要找到要向其中添加值的“父”字典,然后使用最后一个元素将值设置为右键
演示:
请注意,Python PEP8样式指南。上述方法同样适用于列表或字典与列表的混合,因此名称应该是get\u by\u path()
和set\u by\u path()
:
为了完整起见,删除密钥的函数:
def del_by_path(root, items):
"""Delete a key-value in a nested object in root by item sequence."""
del get_by_path(root, items[:-1])[items[-1]]
此库可能有帮助:
用于通过访问和搜索词典的python库
/使用xpath的斜杠/路径
基本上,它可以让你像浏览字典一样浏览字典
文件系统
使用reduce很聪明,但是如果嵌套字典中没有父键,OP的set方法可能会有问题。由于这是我在谷歌搜索中看到的第一篇关于这个主题的SO帖子,我想让它稍微好一点
()中的set方法似乎对缺少父密钥更为健壮。要复制它,请执行以下操作:
def nested_set(dic, keys, value):
for key in keys[:-1]:
dic = dic.setdefault(key, {})
dic[keys[-1]] = value
此外,还可以方便地使用一种方法遍历密钥树并获取我为其创建的所有绝对密钥路径:
def keysInDict(dataDict, parent=[]):
if not isinstance(dataDict, dict):
return [tuple(parent)]
else:
return reduce(list.__add__,
[keysInDict(v,parent+[k]) for k,v in dataDict.items()], [])
它的一个用途是使用以下代码将嵌套树转换为数据帧(假设嵌套字典中的所有叶具有相同的深度)
对
循环使用似乎更像是一个python。
请参阅引用自
已删除reduce()
。如果您确实需要,请使用functools.reduce()
;但是,99%的时候,显式for
循环更具可读性
请注意,接受的解决方案不会设置不存在的嵌套键(它会引发KeyError
)。使用以下方法将创建不存在的节点:
def nested_set(dic, keys, value):
for key in keys[:-1]:
dic = dic.setdefault(key, {})
dic[keys[-1]] = value
代码在Python2和Python3中都可以使用。与其每次都要查找一个值,不如将字典展平一次,然后像b:v:y
def flatten(mydict):
new_dict = {}
for key,value in mydict.items():
if type(value) == dict:
_dict = {':'.join([key, _key]):_value for _key, _value in flatten(value).items()}
new_dict.update(_dict)
else:
new_dict[key]=value
return new_dict
dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}
flat_dict = flatten(dataDict)
print flat_dict
{'b:w': 3, 'b:u': 1, 'b:v:y': 2, 'b:v:x': 1, 'b:v:z': 3, 'a:r': 1, 'a:s': 2, 'a:t': 3}
这样,您只需使用flat_dict['b:v:y']
查找项目,它将为您提供1
不必在每次查找时遍历字典,您可以通过展平字典并保存输出来加快查找速度,这样从冷启动开始的查找就意味着加载展平字典,只需执行键/值查找而不进行遍历。使用递归函数如何
要获取值,请执行以下操作:
def getFromDict(dataDict, maplist):
first, rest = maplist[0], maplist[1:]
if rest:
# if `rest` is not empty, run the function recursively
return getFromDict(dataDict[first], rest)
else:
return dataDict[first]
def setInDict(dataDict, maplist, value):
first, rest = maplist[0], maplist[1:]
if rest:
try:
if not isinstance(dataDict[first], dict):
# if the key is not a dict, then make it a dict
dataDict[first] = {}
except KeyError:
# if key doesn't exist, create one
dataDict[first] = {}
setInDict(dataDict[first], rest, value)
else:
dataDict[first] = value
并设置一个值:
def getFromDict(dataDict, maplist):
first, rest = maplist[0], maplist[1:]
if rest:
# if `rest` is not empty, run the function recursively
return getFromDict(dataDict[first], rest)
else:
return dataDict[first]
def setInDict(dataDict, maplist, value):
first, rest = maplist[0], maplist[1:]
if rest:
try:
if not isinstance(dataDict[first], dict):
# if the key is not a dict, then make it a dict
dataDict[first] = {}
except KeyError:
# if key doesn't exist, create one
dataDict[first] = {}
setInDict(dataDict[first], rest, value)
else:
dataDict[first] = value
纯Python风格,无任何导入:
def nested_set(element, value, *keys):
if type(element) is not dict:
raise AttributeError('nested_set() expects dict as first argument.')
if len(keys) < 2:
raise AttributeError('nested_set() expects at least three arguments, not enough given.')
_keys = keys[:-1]
_element = element
for key in _keys:
_element = _element[key]
_element[keys[-1]] = value
example = {"foo": { "bar": { "baz": "ok" } } }
keys = ['foo', 'bar']
nested_set(example, "yay", *keys)
print(example)
通过递归解决了这个问题:
def get(d,l):
if len(l)==1: return d[l[0]]
return get(d[l[0]],l[1:])
以您的例子:
dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}
maplist1 = ["a", "r"]
maplist2 = ["b", "v", "y"]
print(get(dataDict, maplist1)) # 1
print(get(dataDict, maplist2)) # 2
如果您还希望能够处理任意json(包括嵌套列表和dict),并很好地处理无效的查找路径,那么我的解决方案是:
from functools import reduce
def get_furthest(s, path):
'''
Gets the furthest value along a given key path in a subscriptable structure.
subscriptable, list -> any
:param s: the subscriptable structure to examine
:param path: the lookup path to follow
:return: a tuple of the value at the furthest valid key, and whether the full path is valid
'''
def step_key(acc, key):
s = acc[0]
if isinstance(s, str):
return (s, False)
try:
return (s[key], acc[1])
except LookupError:
return (s, False)
return reduce(step_key, path, (s, True))
def get_val(s, path):
val, successful = get_furthest(s, path)
if successful:
return val
else:
raise LookupError('Invalid lookup path: {}'.format(path))
def set_val(s, path, value):
get_val(s, path[:-1])[path[-1]] = value
如果您不想在其中一个键不存在时引发错误(以便您的主代码可以不中断地运行),则可以使用另一种方法:
在这种情况下,如果任何输入键不存在,则不会返回任何输入键,这可以在主代码中用作检查,以执行替代任务。如何检查并设置dict元素,而不处理所有索引两次
解决方案:
def nested_yield(nested, keys_list):
"""
Get current nested data by send(None) method. Allows change it to Value by calling send(Value) next time
:param nested: list or dict of lists or dicts
:param keys_list: list of indexes/keys
"""
if not len(keys_list): # assign to 1st level list
if isinstance(nested, list):
while True:
nested[:] = yield nested
else:
raise IndexError('Only lists can take element without key')
last_key = keys_list.pop()
for key in keys_list:
nested = nested[key]
while True:
try:
nested[last_key] = yield nested[last_key]
except IndexError as e:
print('no index {} in {}'.format(last_key, nested))
yield None
工作流示例:
ny = nested_yield(nested_dict, nested_address)
data_element = ny.send(None)
if data_element:
# process element
...
else:
# extend/update nested data
ny.send(new_data_element)
...
ny.close()
试验
看到这两个用于设置和获取嵌套属性的静态方法的答案是令人满意的。这些解决方案比使用嵌套树要好得多
这是我的实现
用法:
设置嵌套属性callsattr(my_dict,1,2,3,5)等于my_dict[1][2][3][4]=5
获取嵌套属性调用gattr(my_dict,1,2)
很晚才去聚会,但万一这会对以后的人有所帮助。对于我的用例,下面的函数工作得最好。用于从字典中提取任何数据类型
dict是包含我们价值观的字典
def getnestedvalue(dict, list):
length = len(list)
try:
for depth, key in enumerate(list):
if depth == length - 1:
output = dict[key]
return output
dict = dict[key]
except (KeyError, TypeError):
return None
return None
列表是实现我们价值的“步骤”列表
def getnestedvalue(dict, list):
length = len(list)
try:
for depth, key in enumerate(list):
if depth == length - 1:
output = dict[key]
return output
dict = dict[key]
except (KeyError, TypeError):
return None
return None
连接字符串的方法:
def get_sub_object_from_path(dict_name, map_list):
for i in map_list:
_string = "['%s']" % i
dict_name += _string
value = eval(dict_name)
return value
#Sample:
_dict = {'new': 'person', 'time': {'for': 'one'}}
map_list = ['time', 'for']
print get_sub_object_from_path("_dict",map_list)
#Output:
#one
扩展@DomTomCat和其他人的方法,这些功能(即通过deepcopy返回修改后的数据而不影响输入)setter和mapper适用于嵌套dict
和list
塞特:
def set_at_path(data0, keys, value):
data = deepcopy(data0)
if len(keys)>1:
if isinstance(data,dict):
return {k:(set_by_path(v,keys[1:],value) if k==keys[0] else v) for k,v in data.items()}
if isinstance(data,list):
return [set_by_path(x[1],keys[1:],value) if x[0]==keys[0] else x[1] for x in enumerate(data)]
else:
data[keys[-1]]=value
return data
制图员:
def map_at_path(data0, keys, f):
data = deepcopy(data0)
if len(keys)>1:
if isinstance(data,dict):
return {k:(map_at_path(v,keys[1:],f) if k==keys[0] else v) for k,v in data.items()}
if isinstance(data,list):
return [map_at_path(x[1],keys[1:],f) if x[0]==keys[0] else x[1] for x in enumerate(data)]
else:
data[keys[-1]]=f(data[keys[-1]])
return data
您可以使用python中的eval
函数
def nested_parse(nest, map_list):
nestq = "nest['" + "']['".join(map_list) + "']"
return eval(nestq, {'__builtins__':None}, {'nest':nest})
解释
对于示例查询:maplist=[“b”、“v”、“y”]
nestq
将是“nest['b']['v']['y']”
其中nest
是嵌套字典
eval
内置函数执行给定字符串。但是,重要的是要小心使用eval
功能可能产生的漏洞。可在此处找到讨论:
在nested\u parse()
函数中,我确保没有可用的\uuu内置\uuu
全局变量,只有可用的局部变量是nest
字典 您可以使用pydash:
import pydash as _
_.get(dataDict, ["b", "v", "y"], default='Default')
我用这个
def get_dictionary_value(dictionary_temp, variable_dictionary_keys):
try:
if(len(variable_dictionary_keys) == 0):
return str(dictionary_temp)
variable_dictionary_key = variable_dictionary_keys[0]
variable_dictionary_keys.remove(variable_dictionary_key)
return get_dictionary_value(dictionary_temp[variable_dictionary_key] , variable_dictionary_keys)
except Exception as variable_exception:
logging.error(variable_exception)
return ''
嵌套映射集还应创建不存在的节点、imo:整数键列表、字符串键字典。@user1353510:不同的用例需要不同的行为。这里的代码不是在中创建的
def get_sub_object_from_path(dict_name, map_list):
for i in map_list:
_string = "['%s']" % i
dict_name += _string
value = eval(dict_name)
return value
#Sample:
_dict = {'new': 'person', 'time': {'for': 'one'}}
map_list = ['time', 'for']
print get_sub_object_from_path("_dict",map_list)
#Output:
#one
def set_at_path(data0, keys, value):
data = deepcopy(data0)
if len(keys)>1:
if isinstance(data,dict):
return {k:(set_by_path(v,keys[1:],value) if k==keys[0] else v) for k,v in data.items()}
if isinstance(data,list):
return [set_by_path(x[1],keys[1:],value) if x[0]==keys[0] else x[1] for x in enumerate(data)]
else:
data[keys[-1]]=value
return data
def map_at_path(data0, keys, f):
data = deepcopy(data0)
if len(keys)>1:
if isinstance(data,dict):
return {k:(map_at_path(v,keys[1:],f) if k==keys[0] else v) for k,v in data.items()}
if isinstance(data,list):
return [map_at_path(x[1],keys[1:],f) if x[0]==keys[0] else x[1] for x in enumerate(data)]
else:
data[keys[-1]]=f(data[keys[-1]])
return data
def nested_parse(nest, map_list):
nestq = "nest['" + "']['".join(map_list) + "']"
return eval(nestq, {'__builtins__':None}, {'nest':nest})
import pydash as _
_.get(dataDict, ["b", "v", "y"], default='Default')
def get_dictionary_value(dictionary_temp, variable_dictionary_keys):
try:
if(len(variable_dictionary_keys) == 0):
return str(dictionary_temp)
variable_dictionary_key = variable_dictionary_keys[0]
variable_dictionary_keys.remove(variable_dictionary_key)
return get_dictionary_value(dictionary_temp[variable_dictionary_key] , variable_dictionary_keys)
except Exception as variable_exception:
logging.error(variable_exception)
return ''