1. ホーム
  2. python

[解決済み] ネストされたキーがdictに存在するかどうかをチェックするエレガントな方法?

2022-10-21 12:59:25

質問

各レベルを個別にチェックすることなく、dictに埋もれているキーが存在するかどうかをチェックする、より読みやすい方法はありますか?

埋もれているオブジェクトの中のこの値を取得する必要があるとします(Wikidataから取得した例)。

x = s['mainsnak']['datavalue']['value']['numeric-id']

これがランタイムエラーで終わらないようにするためには、このようにすべてのレベルをチェックする必要があります。

if 'mainsnak' in s and 'datavalue' in s['mainsnak'] and 'value' in s['mainsnak']['datavalue'] and 'nurmeric-id' in s['mainsnak']['datavalue']['value']:
    x = s['mainsnak']['datavalue']['value']['numeric-id']

これを解決するために私が思いつく他の方法は、これを try catch の構成にすることですが、これもこのような単純なタスクとしてはかなり厄介だと感じています。

私はこのようなものを探しています。

x = exists(s['mainsnak']['datavalue']['value']['numeric-id'])

を返します。 True を返します。

どのように解決するのですか?

簡単に説明すると、Pythonでは、信頼する必要があるのは 許可を得るより、許しを得る方が簡単です。

try:
    x = s['mainsnak']['datavalue']['value']['numeric-id']
except KeyError:
    pass


回答

ここでは、ネストしたdictキーに対処する方法を説明します。

def keys_exists(element, *keys):
    '''
    Check if *keys (nested) exists in `element` (dict).
    '''
    if not isinstance(element, dict):
        raise AttributeError('keys_exists() expects dict as first argument.')
    if len(keys) == 0:
        raise AttributeError('keys_exists() expects at least two arguments, one given.')

    _element = element
    for key in keys:
        try:
            _element = _element[key]
        except KeyError:
            return False
    return True

data = {
    "spam": {
        "egg": {
            "bacon": "Well..",
            "sausages": "Spam egg sausages and spam",
            "spam": "does not have much spam in it"
        }
    }
}

print 'spam (exists): {}'.format(keys_exists(data, "spam"))
print 'spam > bacon (do not exists): {}'.format(keys_exists(data, "spam", "bacon"))
print 'spam > egg (exists): {}'.format(keys_exists(data, "spam", "egg"))
print 'spam > egg > bacon (exists): {}'.format(keys_exists(data, "spam", "egg", "bacon"))

出力します。

spam (exists): True
spam > bacon (do not exists): False
spam > egg (exists): True
spam > egg > bacon (exists): True

与えられた element をループし、与えられた順番で各キーをテストします。

私は、すべての variable.get('key', {}) メソッドに続くものであるため、私が見つけた EAFP .

のように呼び出されることを除いた関数です。 keys_exists(dict_element_to_test, 'key_level_0', 'key_level_1', 'key_level_n', ..) . 少なくとも2つの引数、要素と1つのキーが必要ですが、好きなだけキーを追加することができます。

もし、kind of mapを使う必要がある場合は、以下のような方法があります。

expected_keys = ['spam', 'egg', 'bacon']
keys_exists(data, *expected_keys)