176

正しい項目に対処するためにキーのリストを介してアクセスしたい複雑な辞書構造があります。

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
4

18 に答える 18

295

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

そして、完成のために、キーを削除する関数:

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]]
于 2013-02-04T18:07:38.353 に答える
63

ループを使用する方がよりPython的forです。Python3.0の新機能からの引用を参照してください。

削除されreduce()ました。functools.reduce()本当に必要な場合に使用します。ただし、99%の場合、明示的なforループの方が読みやすくなります。

def nested_get(dic, keys):    
    for key in keys:
        dic = dic[key]
    return dic

受け入れられたソリューションは、存在しないネストされたキーを設定しないことに注意してください(それは発生しますKeyError)。以下のアプローチを使用すると、代わりに存在しないノードが作成されます。

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

このコードはPython2と3の両方で機能します。

于 2016-06-08T13:47:04.693 に答える
16

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

また、キーツリーをトラバースして、私が作成したすべての絶対キーパスを取得するメソッドがあると便利な場合があります。

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()], [])

この使用法の1つは、次のコードを使用して、ネストされたツリーを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)
于 2016-04-29T04:38:54.187 に答える
11

このライブラリは役立つ場合があります:https ://github.com/akesterson/dpath-python

/ slashed / pathalaxpathを介して辞書にアクセスして検索するためのPythonライブラリ

基本的には、ファイルシステムであるかのように辞書をグロブできます。

于 2015-04-29T06:02:00.523 に答える
2

値を検索するたびにパフォーマンスヒットを取得する代わりに、辞書を一度フラット化してから、次のようにキーを検索するのはどうでしょうか。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

また、ルックアップごとにディクショナリをトラバースする代わりに、ディクショナリをフラット化して出力を保存することで、これを高速化できる場合があります。これにより、コールドスタートからのルックアップは、フラット化されたディクショナリをロードし、キー/値ルックアップを実行します。トラバーサル。

于 2017-03-01T02:07:58.350 に答える
2

再帰関数を使ってみませんか?

値を取得するには:

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
于 2017-12-08T22:56:27.637 に答える
2

これを再帰で解決しました:

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
于 2018-03-05T13:38:11.057 に答える
1

インポートなしの純粋な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)

出力

{'foo': {'bar': 'yay'}}
于 2018-02-16T14:00:54.440 に答える
1

キーの1つがない場合にエラーを発生させたくない場合の別の方法(メインコードを中断することなく実行できるようにするため):

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が返されます。これは、代替タスクを実行するためのメインコードのチェックとして使用できます。

于 2018-03-16T01:30:53.967 に答える
1

ネストされた属性を設定および取得するための2つの静的メソッドがあることで、これらの答えを見るのは満足のいくものです。これらのソリューションは、ネストされたツリーを使用するよりもはるかに優れています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]
于 2018-12-03T00:56:24.193 に答える
0

ネストされたリストやdictを含む任意の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
于 2018-03-06T21:30:01.930 に答える
0

すべてのインデックスを2回処理せずに、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()
于 2018-08-15T09:46:38.133 に答える
0

パーティーに非常に遅れていますが、これが将来誰かを助けるかもしれない場合に備えて投稿してください。私のユースケースでは、次の関数が最適に機能しました。辞書から任意のデータ型を引き出すために機能します

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
于 2018-12-09T08:43:06.723 に答える
0

pydashを使用できます:

import pydash as _

_.get(dataDict, ["b", "v", "y"], default='Default')

https://pydash.readthedocs.io/en/latest/api.html

于 2020-05-20T19:00:32.307 に答える
0

私はこれを使います

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

于 2021-04-20T17:08:36.613 に答える
-1

文字列を連結する方法:

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
于 2018-12-29T05:10:48.810 に答える
-1

@DomTomCatやその他のアプローチを拡張して、これらの機能的な(つまり、入力に影響を与えずにディープコピーを介して変更されたデータを返す)セッターとマッパーは、ネストされた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
于 2019-05-01T19:00:07.467 に答える
-4

evalこの関数はPythonで利用できます。

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です。議論はここで見つけることができます:

  1. https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
  2. https://www.journaldev.com/22504/python-eval-function

このnested_parse()関数では、__builtins__使用可能なグローバルがなく、使用可能なローカル変数のみがnestディクショナリであることを確認しました。

于 2020-02-05T07:28:11.487 に答える