[python] 키 목록을 통해 중첩 된 사전 항목에 액세스 하시겠습니까?

올바른 항목을 처리하기 위해 키 목록을 통해 액세스하려는 복잡한 사전 구조가 있습니다.

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

maplist = ["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대한 값을 저장할 위치를 찾기 위해 재사용 하십시오 setInDict().

def setInDict(dataDict, mapList, value):
    getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value

마지막 요소를 제외한 모든 요소 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}}

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


답변

  1. 허용 된 솔루션은 python3에서 직접 작동하지 않습니다 from functools import reduce.
  2. 또한 for루프 를 사용하는 것이 더 pythonic처럼 보입니다 . Python 3.0의 새로운 기능 에서 인용 한 내용을 참조하십시오 .

    제거되었습니다 reduce(). functools.reduce()정말로 필요한 경우 사용하십시오 . 그러나 명시 적 for루프가 더 읽기 쉬운 시간의 99 %입니다 .

  3. 다음으로 허용되는 솔루션은 존재하지 않는 중첩 키를 설정하지 않습니다 (을 반환합니다 KeyError)-솔루션에 대한 @eafit의 답변 참조

따라서 kolergy의 질문에서 제안 된 방법을 사용하여 가치를 얻는 것은 어떻습니까?

def getFromDict(dataDict, mapList):    
    for k in mapList: dataDict = dataDict[k]
    return dataDict

그리고 값을 설정하기위한 @eafit의 답변 코드는 다음과 같습니다.

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

둘 다 파이썬 2와 3에서 바로 작동합니다.


답변

reduce 사용은 영리하지만 부모 키가 중첩 된 사전에 존재하지 않으면 OP의 set 메소드에 문제가있을 수 있습니다. 이것은 내 Google 검색 에서이 주제에 대해 본 첫 번째 게시물이므로 조금 더 좋게 만들고 싶습니다.

( 인덱스 및 값 목록이 제공된 중첩 된 파이썬 사전에 값 설정) 의 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()], [])

이를 사용하는 방법은 다음 코드를 사용하여 중첩 트리를 팬더 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)


답변

이 라이브러리가 도움이 될 수 있습니다 : https://github.com/akesterson/dpath-python

/ slashed / paths를 통해 사전에 액세스하고 검색하기위한 Python 라이브러리

기본적으로 파일 시스템 인 것처럼 사전을 넘길 수 있습니다.


답변

재귀 함수를 사용하는 것은 어떻습니까?

가치를 얻으려면 :

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 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)

산출

{'foo': {'bar': 'yay'}}


답변

키 중 하나가없는 경우 오류를 발생시키지 않으려는 다른 방법 (메인 코드가 중단없이 실행될 수 있음) :

def get_value(self,your_dict,*keys):
    curr_dict_ = your_dict
    for k in keys:
        v = curr_dict.get(k,None)
        if v is None:
            break
        if isinstance(v,dict):
            curr_dict = v
    return v

이 경우 입력 키가 없으면 None이 반환되고 기본 작업에서 대체 작업을 수행하기위한 검사로 사용될 수 있습니다.