Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 是否通过键列表访问嵌套字典项?_Python_List_Dictionary - Fatal编程技术网

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()
试验


看到这两个用于设置和获取嵌套属性的静态方法的答案是令人满意的。这些解决方案比使用嵌套树要好得多

这是我的实现

用法

设置嵌套属性call
sattr(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 ''