কীগুলির তালিকার মাধ্যমে নেস্টেড ডিকশনারি আইটেমগুলি অ্যাক্সেস করবেন?


143

আমার কাছে একটি জটিল অভিধান কাঠামো রয়েছে যা আমি সঠিক আইটেমটি সম্বোধনের জন্য কীগুলির একটি তালিকার মাধ্যমে অ্যাক্সেস করতে চাই।

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

উত্তর:


230

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

দ্রষ্টব্য যে পাইথন পিইপি 8 শৈলীর নির্দেশিকা ফাংশনগুলির জন্য সর্প_কেসের নাম লিখে দেয় । উপরোক্ত তালিকা বা অভিধান এবং তালিকার মিশ্রণের জন্য সমানভাবে ভাল কাজ করে, তাই নামগুলি সত্যই হওয়া উচিত 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
স্বেচ্ছাচারিত নেস্টেড স্ট্রাকচারের জন্য এই জাতীয় ট্র্যাভারসিং কতটা নির্ভরযোগ্য? এটিও নেস্টেড তালিকার সাথে মিশ্র অভিধানের জন্য কাজ করবে? ডিফল্ট_মূল্য সরবরাহ করতে এবং ডিফল্ট ডিফল্ট_ভ্যালু কিছুই না হিসাবে আমি কীভাবে getFromDict () পরিবর্তন করব? বহু বছরের পিএইচপি বিকাশ এবং সি বিকাশের আগে আমি পাইথনে নবীন।
দিমিত্রি সিন্টসভ

2
এছাড়াও নেস্টেড ম্যাপ করা সেটটি অ-বিদ্যমান নোডগুলি তৈরি করতে হবে, ইমো: পূর্ণসংখ্যা কীগুলির জন্য তালিকা, স্ট্রিং কীগুলির জন্য অভিধান।
দিমিত্রি সিন্টসভ

1
@ ব্যবহারকারী1353510: এটি যেমন হয়, নিয়মিত সূচীকরণ সিনট্যাক্সটি এখানে ব্যবহৃত হয়, সুতরাং এটি অভিধানগুলির মধ্যেও তালিকা সমর্থন করবে। তাদের জন্য কেবল পূর্ণসংখ্যার সূচকে পাস করুন।
মার্টিজন পিটারস

1
@ user1353510: ডিফল্ট মান, ব্যবহারের জন্য try:, except (KeyError, IndexError): return default_valueবর্তমান প্রায় returnলাইন।
মার্টিজন পিটারস

1
@ জর্জি: dict.get()শব্দার্থবিজ্ঞানের পরিবর্তনগুলি ব্যবহার করে পরিবর্তিত নাম Noneবাদ দেওয়ার পরিবর্তে এটি প্রত্যাবর্তন করে KeyError। পরবর্তী কোনও নাম তারপর একটি ট্রিগার AttributeErroroperatorএটি একটি স্ট্যান্ডার্ড লাইব্রেরি, এখানে এড়াতে হবে না।
মার্টিজন পিটারস

40
  1. গৃহীত সমাধানটি পাইথন 3 এর জন্য সরাসরি কাজ করবে না - এটির প্রয়োজন হবে from functools import reduce
  2. এছাড়াও এটি forলুপ ব্যবহার করা আরও পাইথোনিক বলে মনে হচ্ছে । পাইথন What's.০-এ নতুন কী থেকে উদ্ধৃতিটি দেখুন ।

    সরানো হয়েছে reduce()। আপনার functools.reduce()যদি সত্যিই এটির প্রয়োজন হয় তা ব্যবহার করুন ; তবে, 99 শতাংশ সময় একটি সুস্পষ্ট forলুপ বেশি পঠনযোগ্য।

  3. এর পরে, গৃহীত সমাধান অ-বিদ্যমান নেস্টেড কীগুলি সেট করে না (এটি একটি ফেরত দেয় KeyError) - সমাধানের জন্য @ ইফিতের উত্তর দেখুন

সুতরাং কেন মূল্য পাওয়ার জন্য কুলারগির প্রশ্ন থেকে প্রস্তাবিত পদ্ধতিটি ব্যবহার করবেন না:

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

এবং মান নির্ধারণের জন্য @ ইফিটের উত্তর থেকে কোড:

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

দুজনেই অজগর 2 এবং 3 এ সরাসরি কাজ করে


6
আমি এই সমাধানটি পছন্দ করি - তবে সাবধানতা অবলম্বন করুন। যদি আমি ভুল না হয়ে থাকি, যেহেতু পাইথন অভিধানগুলি অনিবার্য নয় getFromDictতবে কলার ধ্বংস করার সম্ভাবনা থাকে dataDict। আমি copy.deepcopy(dataDict)আগে চাই। অবশ্যই, (লিখিত হিসাবে) এই আচরণটি দ্বিতীয় ফাংশনে পছন্দসই।
ডিলান এফ

15

হ্রাস ব্যবহার করা চালাক, তবে নেপাল অভিধানে প্যারেন্ট কীগুলি প্রাক-বিদ্যমান না থাকলে ওপির সেট পদ্ধতিতে সমস্যা থাকতে পারে। যেহেতু আমার গুগল অনুসন্ধানে আমি এই বিষয়টির জন্য এটি প্রথম এসও পোস্ট, তাই আমি এটি কিছুটা আরও ভাল করতে চাই।

সেট করার পদ্ধতিতে ( সূচক এবং মানের একটি তালিকা দেওয়া নেস্টেড অজগর অভিধানে একটি মান নির্ধারণ করা ) পিতামাতার কীগুলি হারিয়ে যাওয়া আরও দৃ .় বলে মনে হয় । এটি অনুলিপি করতে:

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

এর একটি ব্যবহার নীচের কোডটি ব্যবহার করে নেস্টেড ট্রিটিকে একটি পান্ডাস ডেটা ফ্রেমে রূপান্তরিত করা (ধরে নেওয়া উচিত যে নেস্টেড ডিকশনারির সমস্ত পাতাগুলির একই গভীরতা রয়েছে)।

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)

কেন ইচ্ছামত 'কী' যুক্তি দৈর্ঘ্য 2 বা আরও বেশি সীমাবদ্ধ nested_set?
অ্যালানচলভিটি

10

এই গ্রন্থাগারটি সহায়ক হতে পারে: https://github.com/aketerson/dpath-python

স্ল্যাশ / পাথ আলা xpath এর মাধ্যমে অভিধান অ্যাক্সেস এবং অনুসন্ধানের জন্য একটি অজগর গ্রন্থাগার

মূলত এটি আপনাকে একটি অভিধানে দাবিয়ে তোলে যেন এটি কোনও ফাইল সিস্টেম।


3

পুনরাবৃত্তি ফাংশন ব্যবহার সম্পর্কে?

একটি মান পেতে:

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

2

খাঁটি পাইথন শৈলী, কোনও আমদানি ছাড়াই:

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

2

বিকল্পগুলির মধ্যে যদি আপনি কীগুলির মধ্যে একটি অনুপস্থিত থাকে তবে ত্রুটি বাড়াতে না চান (যাতে আপনার মূল কোডটি বাধা ছাড়াই চলতে পারে):

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

এই ক্ষেত্রে, যদি কোনও ইনপুট কী উপস্থিত না থাকে তবে কোনওটিই ফেরত আসে না, যা বিকল্প কার্য সম্পাদন করতে আপনার মূল কোডটিতে একটি চেক হিসাবে ব্যবহার করা যেতে পারে।


1

প্রতিবার আপনি যখন কোনও মান সন্ধান করতে চান তখন একটি পারফরম্যান্স হিট না করে, আপনার সম্পর্কে অভিধানটি কীভাবে একবার সমতল করা যায় তার পরে কীটি কীভাবে সন্ধান করবেন 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

এবং প্রতিটি অনুসন্ধানে অভিধানটি পরিবর্তনের পরিবর্তে, আপনি অভিধানটি ফ্ল্যাট করে এবং আউটপুট সংরক্ষণ করে এটির গতি বাড়িয়ে তুলতে সক্ষম হবেন যাতে কোল্ড শুরু থেকে দেখার অর্থ সমতল অভিধানটি লোড করা এবং কেবল কোনও কী / মান সন্ধানের সাথে সম্পাদন করা উচিত ট্র্যাভেরসাল।


1

পুনরাবৃত্তি দিয়ে এটি সমাধান করুন:

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

1

কীভাবে চেক সম্পর্কে এবং তারপরে সমস্ত সূচি দু'বার প্রক্রিয়াকরণ না করে ডিক উপাদানটি সেট করবেন?

সমাধান:

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

1

পার্টিতে খুব দেরি হয়ে গেছে তবে ভবিষ্যতে কারও পক্ষে এই পোস্টটি পোস্ট করতে পারে help আমার ব্যবহারের ক্ষেত্রে, নিম্নলিখিত ফাংশনটি সবচেয়ে ভাল কাজ করেছে। অভিধানের বাইরে যে কোনও ডেটা টাইপ টানতে কাজ করে

ডিক হচ্ছে আমাদের মান সম্বলিত অভিধান

তালিকাটি আমাদের মানটির দিকে "পদক্ষেপ" এর একটি তালিকা

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

1

নেস্টেড অ্যাট্রিবিউটগুলি সেট করা ও পাওয়ার জন্য দুটি স্থির পদ্ধতি থাকার জন্য এই উত্তরগুলি দেখে সন্তুষ্টিজনক। এই সমাধানগুলি নেস্টেড ট্রি ব্যবহারের চেয়ে ভাল 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]

1

আমি আপনাকে python-benedictকীপ্যাথ ব্যবহার করে নেস্টেড আইটেমগুলি অ্যাক্সেস করতে ব্যবহার করার পরামর্শ দিই।

এটি ব্যবহার করে ইনস্টল করুন pip:

pip install python-benedict

তারপর:

from benedict import benedict

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

print(dataDict['a.r'])
# or
print(dataDict['a', 'r'])

এখানে সম্পূর্ণ ডকুমেন্টেশন: https://github.com/fabiocaccamo/python-benedict


0

আপনি যদি নেস্টেড তালিকাগুলি এবং ডিক্টস সহ স্বতঃস্ফূর্ত জসনের সাথে কাজ করার দক্ষতা এবং অবৈধভাবে অবৈধ অনুসন্ধানের পথগুলি পরিচালনা করতে চান তবে আমার সমাধানটি এখানে:

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

0

স্ট্রেনটাকেটেট করার জন্য একটি পদ্ধতি:

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

0

@ ডোমটমগ্যাট এবং অন্যের পদ্ধতির প্রসারিত করে এই কার্যকরী (অর্থাত্ ইনপুটকে প্রভাবিত না করে ডিপকপির মাধ্যমে পরিবর্তিত ডেটা ফিরিয়ে দিন) সেটার এবং ম্যাপার নেস্টেড 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

0

আপনি 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নেস্টেড অভিধান নেই।

evalBuiltin ফাংশন দেওয়া পংক্তি সঞ্চালন করে। তবে, evalফাংশন ব্যবহার থেকে উদ্ভূত সম্ভাব্য দুর্বলতাগুলি সম্পর্কে যত্নবান হওয়া গুরুত্বপূর্ণ । আলোচনা এখানে পাওয়া যাবে:

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

ইন nested_parse()ফাংশন, আমি নিশ্চিত যে কোন করেছেন __builtins__globals উপলব্ধ এবং শুধুমাত্র স্থানীয় পরিবর্তনশীল যে পাওয়া যায় হয় nestঅভিধান।


আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.