নেস্টেড অভিধানের মান পাওয়ার জন্য পাইথন নিরাপদ পদ্ধতি method


144

আমার একটি নেস্টেড ডিকশনারি আছে। নিরাপদে মান বের করার একমাত্র উপায় কি?

try:
    example_dict['key1']['key2']
except KeyError:
    pass

অথবা অজগরটির কাছে get()নেস্টেড ডিকশনারির মতো একটি পদ্ধতি রয়েছে ?



1
আপনার প্রশ্নের কোডটি আমার মতে, অভিধান থেকে নেস্টেড মানগুলি পাওয়ার জন্য ইতিমধ্যে সর্বোত্তম উপায়। আপনি সবসময় except keyerror:ক্লজে একটি ডিফল্ট মান নির্দিষ্ট করতে পারেন ।
পিটার শর্ন

উত্তর:


280

আপনি getদুবার ব্যবহার করতে পারেন :

example_dict.get('key1', {}).get('key2')

Noneহয় key1বা key2না থাকলে এটি ফিরে আসবে ।

মনে রাখবেন, এই এখনও একটি বাড়াতে পারে AttributeErrorযদি example_dict['key1']বিদ্যমান কিন্তু একটি অভি (বা একটি অভি মত বস্তু একটি পক্ষে নয়, সে getপদ্ধতি)। try..exceptআপনি যে কোডটি পোস্ট একটি বাড়াতে হবে TypeErrorপরিবর্তে যদি example_dict['key1']unsubscriptable হয়।

আর একটি পার্থক্য হ'ল try...exceptপ্রথম অনুপস্থিত কী এর সাথে সাথেই শর্ট সার্কিট। getকল অফ চেইন না।


আপনি যদি সিনট্যাক্স সংরক্ষণ করতে চান, example_dict['key1']['key2']তবে এটি কখনও কী-এরফারগুলি বাড়িয়ে তুলতে চান না, তবে আপনি হ্যাশার রেসিপিটি ব্যবহার করতে পারেন :

class Hasher(dict):
    # https://stackoverflow.com/a/3405143/190597
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

example_dict = Hasher()
print(example_dict['key1'])
# {}
print(example_dict['key1']['key2'])
# {}
print(type(example_dict['key1']['key2']))
# <class '__main__.Hasher'>

নোট করুন যে কীটি নিখোঁজ থাকা অবস্থায় এটি একটি খালি হাশর ফেরত দেয়।

যেহেতু Hasherহয় একটি উপশ্রেণী dictআপনি একই ভাবে একটি Hasher ব্যবহার করতে পারেন আপনি একটি ব্যবহার করতে পারে dict। সমস্ত একই পদ্ধতি এবং বাক্য গঠন উপলব্ধ, হাশাররা অনুপস্থিত কীগুলি আলাদাভাবে আচরণ করে।

আপনি একটি নিয়মিত রূপান্তর করতে পারেন dictএকটি মধ্যে Hasherভালো:

hasher = Hasher(example_dict)

এবং Hasherএকটি নিয়মিত dictহিসাবে সহজেই রূপান্তর করুন :

regular_dict = dict(hasher)

আরেকটি বিকল্প হ'ল সহায়তা কর্মে কদর্যতা লুকানো:

def safeget(dct, *keys):
    for key in keys:
        try:
            dct = dct[key]
        except KeyError:
            return None
    return dct

সুতরাং আপনার বাকী কোড অপেক্ষাকৃত পঠনযোগ্য থাকতে পারে:

safeget(example_dict, 'key1', 'key2')

37
সুতরাং, অজগর এই ক্ষেত্রে সুন্দর সমাধান নেই? :(
আরতি

আমি একই ধরণের বাস্তবায়ন নিয়ে একটি সমস্যায় পড়েছি। আপনার যদি d = {কী 1: কিছুই নেই}, প্রথম প্রাপ্তি কোনওটিই ফিরে আসবে না এবং তারপরে আপনার একটি ব্যতিক্রম হবে): আমি এটির সমাধান বের করার চেষ্টা করছি
হুয়েরসিও

1
safegetযেহেতু এটি মূল অভিধান মুছে ফেলা হয়, যার অর্থ তুমি কি নিরাপদে মত জিনিসগুলি করতে পারেন পদ্ধতি উপায়ে খুব নিরাপদ নয় অনেকটা হয় safeget(dct, 'a', 'b') or safeget(dct, 'a')
neverfox

4
@ কুর্টবাউরবাাকি: স্থানীয় ভেরিয়েবলে নতুন মানটিকে dct = dct[key] পুনরায় সাইন করে । এটি আসল ডিকটি রূপান্তরিত করে না (সুতরাং মূল ডিকটি দ্বারা প্রভাবিত হয় না )) অন্যদিকে, যদি ব্যবহৃত হয়, তবে মূল ডিকটি সংশোধন করা হত। অন্য কথায়, পাইথনের নামগুলি মানগুলির সাথে আবদ্ধ dctsafegetdct[key] = ... । একটি নাম একটি নতুন মান অ্যাসাইনমেন্ট পুরাতন মান প্রভাবিত করে না (যদি না সেখানে পুরাতন মান আর রেফারেন্স, যে ক্ষেত্রে (CPython মধ্যে) এটা আবর্জনা সংগ্রহ করা হবে আছে।)
unutbu

1
safegetপদ্ধতি একটি নেস্টেড অভি চাবি ক্ষেত্রে ব্যর্থ হবে বিদ্যমান, কিন্তু মান নাল। এটি TypeError: 'NoneType' object is not subscriptableপরবর্তী পুনরাবৃত্তিতে ছুড়ে ফেলবে
স্ট্যানলে এফ।

60

এছাড়াও আপনি ব্যবহার পাইথন পারে কমাতে :

def deep_get(dictionary, *keys):
    return reduce(lambda d, key: d.get(key) if d else None, keys, dictionary)

5
কেবল উল্লেখ করতে চেয়েছিলাম যে ফান্টুলগুলি পাইথন 3 এ আর কোনও বিল্টিন নয় এবং ফান্টুলগুলি থেকে আমদানি করা দরকার, যা এই পদ্ধতিকে কিছুটা কম মার্জিত করে তোলে।
yoniLavi

3
এই মন্তব্যে সামান্য সংশোধন: হ্রাস এখন পাই 3-তে অন্তর্নির্মিত নয়। তবে আমি দেখতে পাচ্ছি না কেন এটি এটিকে কম মার্জিত করে তোলে। এটি ওয়ান-লাইনারের পক্ষে এটি কম উপযুক্ত করে তোলে, তবে ওয়ান-লাইনার হওয়ায় স্বয়ংক্রিয়ভাবে কোনও কিছুকে "মার্জিত" হিসাবে যোগ্যতা বা অযোগ্য ঘোষণা করে না।
PaulMcG

30

এখানে এই সমস্ত উত্তর এবং আমার করা ছোট ছোট পরিবর্তনগুলি একত্রিত করে আমি মনে করি এই ফাংশনটি কার্যকর হবে। এটি নিরাপদ, দ্রুত, সহজেই রক্ষণাবেক্ষণযোগ্য।

def deep_get(dictionary, keys, default=None):
    return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)

উদাহরণ:

>>> from functools import reduce
>>> def deep_get(dictionary, keys, default=None):
...     return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)
...
>>> person = {'person':{'name':{'first':'John'}}}
>>> print (deep_get(person, "person.name.first"))
John
>>> print (deep_get(person, "person.name.lastname"))
None
>>> print (deep_get(person, "person.name.lastname", default="No lastname"))
No lastname
>>>

1
জিনজা 2 টেম্পলেটগুলির জন্য উপযুক্ত
টমাস

এটির একটি অসুবিধা থাকা সত্ত্বেও এটি একটি ভাল সমাধান: প্রথম কীটি উপলব্ধ না থাকলেও, বা ফাংশনটির অভিধান আর্গুমেন্ট হিসাবে মানটি পাস হলেও অভিধানটি প্রথম উপাদান থেকে শেষের দিকে যাবে the মূলত, এটি সব ক্ষেত্রেই এটি করে।
আর্সেনি

1
deep_get({'a': 1}, "a.b")দেয় Noneতবে আমি এর মতো KeyErrorবা অন্য কিছু ব্যতিক্রম আশা করব expect
stackunderflow

@edityouprofile। তারপর আপনি শুধু থেকে পরিবর্তন ফেরত মান ছোট পরিবর্তন করতে প্রয়োজন NoneথেকেRaise KeyError
Yuda Prawira

15

একটি আরও সুরক্ষিত পদ্ধতির Yoav এর উত্তর উপর ভিত্তি করে:

def deep_get(dictionary, *keys):
    return reduce(lambda d, key: d.get(key, None) if isinstance(d, dict) else None, keys, dictionary)

12

একটি পুনরাবৃত্তি সমাধান। এটি সর্বাধিক দক্ষ নয় তবে আমি এটি অন্যান্য উদাহরণগুলির চেয়ে কিছুটা বেশি পঠনযোগ্য এবং এটি ফান্টুলগুলিতে নির্ভর করে না।

def deep_get(d, keys):
    if not keys or d is None:
        return d
    return deep_get(d.get(keys[0]), keys[1:])

উদাহরণ

d = {'meta': {'status': 'OK', 'status_code': 200}}
deep_get(d, ['meta', 'status_code'])     # => 200
deep_get(d, ['garbage', 'status_code'])  # => None

আরও পালিশ করা সংস্করণ

def deep_get(d, keys, default=None):
    """
    Example:
        d = {'meta': {'status': 'OK', 'status_code': 200}}
        deep_get(d, ['meta', 'status_code'])          # => 200
        deep_get(d, ['garbage', 'status_code'])       # => None
        deep_get(d, ['meta', 'garbage'], default='-') # => '-'
    """
    assert type(keys) is list
    if d is None:
        return default
    if not keys:
        return d
    return deep_get(d.get(keys[0]), keys[1:], default)

8

যদিও হ্রাস পদ্ধতির ঝরঝরে এবং সংক্ষিপ্ত, আমি মনে করি একটি সহজ লুপ আঁকাবাঁকা করা সহজ। আমি একটি ডিফল্ট প্যারামিটারও অন্তর্ভুক্ত করেছি।

def deep_get(_dict, keys, default=None):
    for key in keys:
        if isinstance(_dict, dict):
            _dict = _dict.get(key, default)
        else:
            return default
    return _dict

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

def deep_get(_dict, keys, default=None):

    def _reducer(d, key):
        if isinstance(d, dict):
            return d.get(key, default)
        return default

    return reduce(_reducer, keys, _dict)

ব্যবহার

nested = {'a': {'b': {'c': 42}}}

print deep_get(nested, ['a', 'b'])
print deep_get(nested, ['a', 'b', 'z', 'z'], default='missing')

5

আমি আপনাকে চেষ্টা করার পরামর্শ দিচ্ছি python-benedict

এটি একটি dictসাবক্লাস যা কীপ্যাথ সমর্থন এবং আরও অনেক কিছু সরবরাহ করে।

স্থাপন: pip install python-benedict

from benedict import benedict

example_dict = benedict(example_dict, keypath_separator='.')

এখন আপনি কীপ্যাথ ব্যবহার করে নেস্টেড মানগুলি অ্যাক্সেস করতে পারেন :

val = example_dict['key1.key2']

# using 'get' method to avoid a possible KeyError:
val = example_dict.get('key1.key2')

বা কী তালিকা ব্যবহার করে নেস্টেড মানগুলিতে অ্যাক্সেস করুন :

val = example_dict['key1', 'key2']

# using get to avoid a possible KeyError:
val = example_dict.get(['key1', 'key2'])

এটি গিটহাবের উপর ভাল পরীক্ষা করা এবং ওপেন সোর্স :

https://github.com/fabiocaccamo/python-benedict


@ perfecto25 আপনাকে ধন্যবাদ! আমি শীঘ্রই নতুন বৈশিষ্ট্য প্রকাশ করব, সাথে থাকব 😉
ফ্যাবিও ক্যাকামো

@ পারফেক্টো 25 আমি সূচকগুলি তালিকাতে সমর্থন যুক্ত করেছি, যেমন। d.get('a.b[0].c[-1]')
ফ্যাবিও ক্যাকামো

4

একটি সরল শ্রেণি যা একটি ডিকটি মোড়ানো করতে পারে এবং একটি চাবির উপর ভিত্তি করে পুনরুদ্ধার করতে পারে:

class FindKey(dict):
    def get(self, path, default=None):
        keys = path.split(".")
        val = None

        for key in keys:
            if val:
                if isinstance(val, list):
                    val = [v.get(key, default) if v else None for v in val]
                else:
                    val = val.get(key, default)
            else:
                val = dict.get(self, key, default)

            if not val:
                break

        return val

উদাহরণ স্বরূপ:

person = {'person':{'name':{'first':'John'}}}
FindDict(person).get('person.name.first') # == 'John'

যদি কীটি বিদ্যমান না থাকে তবে এটি Noneডিফল্টরূপে ফিরে আসে । আপনি মোড়কে default=কী ব্যবহার করে ওভাররাইড করতে পারেন FindDict- উদাহরণস্বরূপ:

FindDict(person, default='').get('person.name.last') # == doesn't exist, so ''

3

দ্বিতীয় স্তরের কী পুনরুদ্ধারের জন্য, আপনি এটি করতে পারেন:

key2_value = (example_dict.get('key1') or {}).get('key2')

2

দেখার পর এই গভীরভাবে পেয়ে বৈশিষ্ট্যাবলী জন্য, আমি নিম্নলিখিত নিরাপদে নেস্টেড পেতে প্রণীত dictডট স্বরলিপি ব্যবহার মান। এটি আমার dictsপক্ষে কাজ করে কারণ আমার মোংগোডিবি অবজেক্টগুলি ডিসরিয়ালাইজ করা হয়েছে, তাই আমি জানি যে মূল নামগুলিতে সেগুলি নেই .। এছাড়াও, আমার প্রসঙ্গে, আমি একটি মিথ্যা ফ্যালব্যাক মান ( None) যা আমার ডেটাতে নেই তা নির্দিষ্ট করতে পারি, তাই ফাংশনটি কল করার সময় আমি চেষ্টা / প্যাটার্ন বাদ দিয়ে এড়াতে পারি।

from functools import reduce # Python 3
def deepgetitem(obj, item, fallback=None):
    """Steps through an item chain to get the ultimate value.

    If ultimate value or path to value does not exist, does not raise
    an exception and instead returns `fallback`.

    >>> d = {'snl_final': {'about': {'_icsd': {'icsd_id': 1}}}}
    >>> deepgetitem(d, 'snl_final.about._icsd.icsd_id')
    1
    >>> deepgetitem(d, 'snl_final.about._sandbox.sbx_id')
    >>>
    """
    def getitem(obj, name):
        try:
            return obj[name]
        except (KeyError, TypeError):
            return fallback
    return reduce(getitem, item.split('.'), obj)

7
fallbackআসলে ফাংশন ব্যবহার করা হয় না।
153957

নোট করুন যে এটি.
JW যুক্ত

যখন আমরা আপত্তি [নাম] কল করি না কেন আপত্তি করুন না (নাম, ফালব্যাক) এবং চেষ্টাটি এড়ানোর চেষ্টা করুন (যদি আপনি চেষ্টাটি ধরতে চান তবে
ফ্যালব্যাক

ধন্যবাদ @ 153957। আমি এটা ঠিক করেছি. এবং হ্যাঁ @ জেডাব্লু, এটি আমার ব্যবহারের ক্ষেত্রে কাজ করে। sep=','প্রদত্ত (সিপ, ফালব্যাক) শর্তের জন্য সাধারণকরণের জন্য আপনি একটি কীওয়ার্ড আরগ যুক্ত করতে পারেন । এবং @denvar, যদি হ্রাসের ক্রম অনুসারে objটাইপের কথা বলা হয় , তবে আপত্তি int[নাম] একটি টাইপরর উত্থাপন করে, যা আমি ধরা করি। আমি যদি এর পরিবর্তে অবজেক্ট (নাম) বা আপজ.জেট (নাম, ফালব্যাক) ব্যবহার করি তবে এটি একটি অ্যাট্রিবিউটআরারের উত্থাপন করবে, সুতরাং যে কোনও উপায়েই আমাকে ধরতে হবে।
ডনি উইনস্টন

1

তবুও একই জিনিসটির জন্য অন্য একটি ফাংশন, কীটি পাওয়া গিয়েছিল কিনা এবং উপস্থাপন করতে কিছু বুলিয়ান দেয় এবং কিছু অপ্রত্যাশিত ত্রুটি পরিচালনা করে।

'''
json : json to extract value from if exists
path : details.detail.first_name
            empty path represents root

returns a tuple (boolean, object)
        boolean : True if path exists, otherwise False
        object : the object if path exists otherwise None

'''
def get_json_value_at_path(json, path=None, default=None):

    if not bool(path):
        return True, json
    if type(json) is not dict :
        raise ValueError(f'json={json}, path={path} not supported, json must be a dict')
    if type(path) is not str and type(path) is not list:
        raise ValueError(f'path format {path} not supported, path can be a list of strings like [x,y,z] or a string like x.y.z')

    if type(path) is str:
        path = path.strip('.').split('.')
    key = path[0]
    if key in json.keys():
        return get_json_value_at_path(json[key], path[1:], default)
    else:
        return False, default

উদাহরণস্বরূপ ব্যবহার:

my_json = {'details' : {'first_name' : 'holla', 'last_name' : 'holla'}}
print(get_json_value_at_path(my_json, 'details.first_name', ''))
print(get_json_value_at_path(my_json, 'details.phone', ''))

(সত্য, 'হোলা')

(মিথ্যা, '')



0

আনটবুর উত্তরের এমন একটি অভিযোজন যা আমি আমার নিজস্ব কোডে দরকারী পেয়েছি:

example_dict.setdefaut('key1', {}).get('key2')

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


0

যেহেতু কোনও একটি কী কী অনুপস্থিত থাকলে একটি মূল ত্রুটি উত্থাপন করা যুক্তিসঙ্গত কাজ, তাই আমরা এটির জন্য যাচাই করতে পারি না এবং এটি এর মতো একক পেতে পারি না:

def get_dict(d, kl):
  cur = d[kl[0]]
  return get_dict(cur, kl[1:]) if len(kl) > 1 else cur

0

reduceএটিকে তালিকার সাথে কাজ করার জন্য যোগাযোগের সামান্য উন্নতি । অ্যারের পরিবর্তে বিন্দু দ্বারা বিভাজিত স্ট্রিং হিসাবে ডেটা পাথ ব্যবহার করা।

def deep_get(dictionary, path):
    keys = path.split('.')
    return reduce(lambda d, key: d[int(key)] if isinstance(d, list) else d.get(key) if d else None, keys, dictionary)

0

আমি যে সমাধানটি ব্যবহার করেছি এটি ডাবল গেটের সমান, তবে অন্য যুক্তি যদি ব্যবহার করে টাইপআরার এড়ানোর অতিরিক্ত দক্ষতার সাথে থাকে:

    value = example_dict['key1']['key2'] if example_dict.get('key1') and example_dict['key1'].get('key2') else default_value

যাইহোক, অভিধানটি যত ঘৃণিত হয় তত বেশি জটিল হয়ে ওঠে।


0

নেস্টেড ডিকশনারি / জেএসএন লকআপের জন্য, আপনি ডিক্টর ব্যবহার করতে পারেন

পাইপ ইনস্টল ডিক্টর

ডিক অবজেক্ট

{
    "characters": {
        "Lonestar": {
            "id": 55923,
            "role": "renegade",
            "items": [
                "space winnebago",
                "leather jacket"
            ]
        },
        "Barfolomew": {
            "id": 55924,
            "role": "mawg",
            "items": [
                "peanut butter jar",
                "waggy tail"
            ]
        },
        "Dark Helmet": {
            "id": 99999,
            "role": "Good is dumb",
            "items": [
                "Shwartz",
                "helmet"
            ]
        },
        "Skroob": {
            "id": 12345,
            "role": "Spaceballs CEO",
            "items": [
                "luggage"
            ]
        }
    }
}

লোনস্টারের আইটেমগুলি পেতে, কেবল একটি বিন্দু-বিভক্ত পথ সরবরাহ করুন, অর্থাত্‍

import json
from dictor import dictor

with open('test.json') as data: 
    data = json.load(data)

print dictor(data, 'characters.Lonestar.items')

>> [u'space winnebago', u'leather jacket']

কীটি যদি পথে না আসে তবে আপনি ফলব্যাক মান সরবরাহ করতে পারেন

আপনি করতে পারেন এমন আরও অনেকগুলি বিকল্প রয়েছে যেমন লেটার কেসিং উপেক্ষা করা এবং 'ছাড়াও অন্যান্য অক্ষর ব্যবহার করা।' পথ বিভাজক হিসাবে,

https://github.com/perfecto25/dictor


0

আমি এই উত্তরটি সামান্যই পরিবর্তন করেছি । আমরা সংখ্যার সাথে তালিকাটি ব্যবহার করছি কিনা তা পরীক্ষা করে যুক্ত করেছি। সুতরাং এখন আমরা এটি যে কোনও উপায়ে ব্যবহার করতে পারি। deep_get(allTemp, [0], {})বা deep_get(getMinimalTemp, [0, minimalTemperatureKey], 26)ইত্যাদি

def deep_get(_dict, keys, default=None):
    def _reducer(d, key):
        if isinstance(d, dict):
            return d.get(key, default)
        if isinstance(d, list):
            return d[key] if len(d) > 0 else default
        return default
    return reduce(_reducer, keys, _dict)

0

ইতিমধ্যে বেশ কয়েকটি ভাল উত্তর রয়েছে তবে আমি জাভাস্ক্রিপ্টের জমিতে লোডাশের অনুরূপ get নামক একটি ফাংশন নিয়ে এসেছি যা সূচকে তালিকাতে পৌঁছানোর পক্ষে সমর্থন করে:

def get(value, keys, default_value = None):
'''
    Useful for reaching into nested JSON like data
    Inspired by JavaScript lodash get and Clojure get-in etc.
'''
  if value is None or keys is None:
      return None
  path = keys.split('.') if isinstance(keys, str) else keys
  result = value
  def valid_index(key):
      return re.match('^([1-9][0-9]*|[0-9])$', key) and int(key) >= 0
  def is_dict_like(v):
      return hasattr(v, '__getitem__') and hasattr(v, '__contains__')
  for key in path:
      if isinstance(result, list) and valid_index(key) and int(key) < len(result):
          result = result[int(key)] if int(key) < len(result) else None
      elif is_dict_like(result) and key in result:
          result = result[key]
      else:
          result = default_value
          break
  return result

def test_get():
  assert get(None, ['foo']) == None
  assert get({'foo': 1}, None) == None
  assert get(None, None) == None
  assert get({'foo': 1}, []) == {'foo': 1}
  assert get({'foo': 1}, ['foo']) == 1
  assert get({'foo': 1}, ['bar']) == None
  assert get({'foo': 1}, ['bar'], 'the default') == 'the default'
  assert get({'foo': {'bar': 'hello'}}, ['foo', 'bar']) == 'hello'
  assert get({'foo': {'bar': 'hello'}}, 'foo.bar') == 'hello'
  assert get({'foo': [{'bar': 'hello'}]}, 'foo.0.bar') == 'hello'
  assert get({'foo': [{'bar': 'hello'}]}, 'foo.1') == None
  assert get({'foo': [{'bar': 'hello'}]}, 'foo.1.bar') == None
  assert get(['foo', 'bar'], '1') == 'bar'
  assert get(['foo', 'bar'], '2') == None
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.