通过键列表访问嵌套的字典项?

kol*_*rgy 128 python dictionary list

我有一个复杂的字典结构,我想通过一个键列表访问,以解决正确的项目.

dataDict = {
    "a":{
        "r": 1,
        "s": 2,
        "t": 3
        },
    "b":{
        "u": 1,
        "v": {
            "x": 1,
            "y": 2,
            "z": 3
        },
        "w": 3
        }
}    

maplist = ["a", "r"]
Run Code Online (Sandbox Code Playgroud)

要么

maplist = ["b", "v", "y"]
Run Code Online (Sandbox Code Playgroud)

我已经制作了以下代码,但是我确信如果有人有想法,有更好更有效的方法.

# 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
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 196

使用reduce()遍历词典:

from functools import reduce  # forward compatibility for Python 3
import operator

def getFromDict(dataDict, mapList):
    return reduce(operator.getitem, mapList, dataDict)
Run Code Online (Sandbox Code Playgroud)

并重getFromDict用以查找存储值的位置setInDict():

def setInDict(dataDict, mapList, value):
    getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value
Run Code Online (Sandbox Code Playgroud)

除了最后一个元素之外的所有元素mapList都需要找到要添加值的"父"字典,然后使用最后一个元素将值设置为右键.

演示:

>>> getFromDict(dataDict, ["a", "r"])
1
>>> getFromDict(dataDict, ["b", "v", "y"])
2
>>> setInDict(dataDict, ["b", "v", "w"], 4)
>>> import pprint
>>> pprint.pprint(dataDict)
{'a': {'r': 1, 's': 2, 't': 3},
 'b': {'u': 1, 'v': {'w': 4, 'x': 1, 'y': 2, 'z': 3}, 'w': 3}}
Run Code Online (Sandbox Code Playgroud)

请注意,Python PEP8样式指南规定了函数的snake_case名称.以上内容同样适用于列表或字典和列表的混合,因此名称应该是get_by_path()set_by_path():

from functools import reduce  # forward compatibility for Python 3
import operator

def get_by_path(root, items):
    """Access a nested object in root by item sequence."""
    return reduce(operator.getitem, items, root)

def set_by_path(root, items, value):
    """Set a value in a nested object in root by item sequence."""
    get_by_path(root, items[:-1])[items[-1]] = value
Run Code Online (Sandbox Code Playgroud)

  • 嵌套映射集还应创建不存在的节点imo:整数键列表,字符串键字典。 (2认同)

Dom*_*Cat 31

  1. 接受的解决方案不能直接用于python3 - 它需要一个from functools import reduce.
  2. 使用for循环似乎更pythonic .请参阅Python 3.0中的新功能.

    删除了reduce().使用,functools.reduce()如果你真的需要它; 但是,99%的时间显式for循环更具可读性.

  3. 接下来,接受的解决方案不会设置不存在的嵌套键(它返回一个KeyError) - 请参阅@ eafit的解决方案的答案

那么为什么不使用kolergy的问题中建议的方法来获取值:

def getFromDict(dataDict, mapList):    
    for k in mapList: dataDict = dataDict[k]
    return dataDict
Run Code Online (Sandbox Code Playgroud)

以及来自@ eafit设置值的答案的代码:

def nested_set(dic, keys, value):
    for key in keys[:-1]:
        dic = dic.setdefault(key, {})
    dic[keys[-1]] = value
Run Code Online (Sandbox Code Playgroud)

两者都在python 2和3中直接工作

  • 我更喜欢这种解决方案-但要小心。如果我没记错的话,由于Python词典不是不可变的,``getFromDict``有可能破坏调用者的``dataDict''。我会先``copy.deepcopy(dataDict)``。当然,(如所写)在第二个函数中需要此行为。 (3认同)
  • @DylanF 好的,我现在明白了。但这不是“getFromDict”本身破坏了调用者的“dataDict”吗?这是由于改变了返回值,这是在函数外部完成的。如果用户不想要的话,他们总是可以制作副本,但是无法撤消在函数内制作的副本 - 因此不复制会更灵活。 (2认同)

eaf*_*fit 13

使用reduce很聪明,但如果父键在嵌套字典中不存在,则OP的set方法可能会出现问题.由于这是我在谷歌搜索中看到的第一篇SO帖子,我想稍微好一些.

(在给定索引和值列表的情况下,在嵌套python字典中设置值)中的set方法对于丢失的父键似乎更健壮.复制它:

def nested_set(dic, keys, value):
    for key in keys[:-1]:
        dic = dic.setdefault(key, {})
    dic[keys[-1]] = value
Run Code Online (Sandbox Code Playgroud)

此外,有一个方法可以方便地遍历密钥树并获取我创建的所有绝对密钥路径:

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()], [])
Run Code Online (Sandbox Code Playgroud)

它的一个用途是使用以下代码将嵌套树转换为pandas DataFrame(假设嵌套字典中的所有叶子具有相同的深度).

def dict_to_df(dataDict):
    ret = []
    for k in keysInDict(dataDict):
        v = np.array( getFromDict(dataDict, k), )
        v = pd.DataFrame(v)
        v.columns = pd.MultiIndex.from_product(list(k) + [v.columns])
        ret.append(v)
    return reduce(pd.DataFrame.join, ret)
Run Code Online (Sandbox Code Playgroud)


DMf*_*fll 9

这个库可能会有所帮助:https://github.com/akesterson/dpath-python

一个python库,用于通过/ slashed/paths ala xpath访问和搜索字典

基本上它可以让你在字典上覆盖,就像它是一个文件系统一样.


xyr*_*res 6

使用递归函数怎么样?

获取一个值:

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]
Run Code Online (Sandbox Code Playgroud)

并设置一个值:

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
Run Code Online (Sandbox Code Playgroud)