키 목록을 통해 중첩 된 사전 항목에 액세스 하시겠습니까?
올바른 항목을 처리하기 위해 키 목록을 통해 액세스하려는 복잡한 사전 구조가 있습니다.
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
- 허용 된 솔루션은 python3에서 직접 작동하지 않습니다
from functools import reduce
. - 또한
for
루프 를 사용하는 것이 더 pythonic처럼 보입니다 . Python 3.0의 새로운 기능 에서 인용 한 내용을 참조하십시오 .제거되었습니다
reduce()
.functools.reduce()
정말로 필요한 경우 사용하십시오 . 그러나 명시 적for
루프가 더 읽기 쉬운 시간의 99 %입니다 . - 다음으로 허용되는 솔루션은 존재하지 않는 중첩 키를 설정하지 않습니다 (을 반환합니다
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이 반환되고 기본 작업에서 대체 작업을 수행하기위한 검사로 사용될 수 있습니다.
값을 찾을 때마다 성능을 높이는 대신 사전을 한 번 평평하게 한 다음 키를 간단히 찾는 방법은 어떻습니까? 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
.
또한 각 조회에서 사전을 순회하는 대신 사전을 평평하게하고 출력을 저장하여 콜드 스타트에서 조회하면 평평한 사전을로드하고 단순히 키 / 값 조회를 수행하지 않음으로써 속도를 높일 수 있습니다 순회.
모든 인덱스를 두 번 처리하지 않고 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()
테스트
>>> cfg= {'Options': [[1,[0]],[2,[4,[8,16]]],[3,[9]]]}
ny = nested_yield(cfg, ['Options',1,1,1])
ny.send(None)
[8, 16]
>>> ny.send('Hello!')
'Hello!'
>>> cfg
{'Options': [[1, [0]], [2, [4, 'Hello!']], [3, [9]]]}
>>> ny.close()
파티에 매우 늦었지만 나중에이를 위해 도움이 될 수있는 경우에 게시합니다. 내 유스 케이스의 경우 다음 기능이 가장 효과적이었습니다. 사전에서 모든 데이터 유형을 가져옵니다.
dict 는 우리의 가치를 담고있는 사전입니다
list 는 우리의 가치를 향한 "단계"의 목록입니다
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(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
중첩 된 목록과 dicts를 포함하여 임의의 json으로 작업하고 유효하지 않은 조회 경로를 잘 처리 할 수 있기를 원한다면 여기 내 해결책이 있습니다.
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
문자열을 연결하는 방법 :
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
중첩 된 속성을 설정하고 얻는 두 가지 정적 방법을 사용하면이 답변을 보는 것이 만족 스럽습니다. 이 솔루션은 중첩 트리를 사용하는 것보다 낫습니다 https://gist.github.com/hrldcpr/2012250
여기 내 구현이 있습니다.
사용법 :
중첩 된 속성 호출을 설정하려면 sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5
중첩 된 속성 호출을 얻으려면 gattr(my_dict, 1, 2)
def gattr(d, *attrs):
"""
This method receives a dict and list of attributes to return the innermost value of the give dict
"""
try:
for at in attrs:
d = d[at]
return d
except(KeyError, TypeError):
return None
def sattr(d, *attrs):
"""
Adds "val" to dict in the hierarchy mentioned via *attrs
For ex:
sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
This method creates necessary objects until it reaches the final depth
This behaviour is also known as autovivification and plenty of implementation are around
This implementation addresses the corner case of replacing existing primitives
https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
"""
for attr in attrs[:-2]:
if type(d.get(attr)) is not dict:
d[attr] = {}
d = d[attr]
d[attrs[-2]] = attrs[-1]
Extending @DomTomCat and others' approach, these functional (ie, return modified data via deepcopy without affecting the input) setter and mapper works for nested dict
and list
.
setter:
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
mapper:
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
참고URL : https://stackoverflow.com/questions/14692690/access-nested-dictionary-items-via-a-list-of-keys
'development' 카테고리의 다른 글
익명 형식을 매개 변수로 전달하는 방법은 무엇입니까? (0) | 2020.07.09 |
---|---|
요소의 내용이 넘치지 않는가? (0) | 2020.07.09 |
Angular2 @ get / set 속성으로 입력 (0) | 2020.07.09 |
C ++ 11에서 "auto"로 추론 할 때 람다 유형은 무엇입니까? (0) | 2020.07.09 |
사람들은 화석 DVCS에 대해 어떻게 생각합니까? (0) | 2020.07.09 |