检查dict中是否存在嵌套键的优雅方法?

2024-05-19 21:38:20 发布

您现在位置:Python中文网/ 问答频道 /正文

是否有更可读的方法来检查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


Tags: and对象方法inid示例value密钥
3条回答

简而言之,对于Python,您必须相信它是easier to ask for forgiveness than permission

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', ..)。至少需要两个参数,元素和一个键,但可以添加所需的键数

如果需要使用某种类型的地图,可以执行以下操作:

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

“尝试/例外”似乎是实现这一点的最具python风格的方式。
以下递归函数应该可以工作(如果在dict中找不到其中一个键,则返回None):

def exists(obj, chain):
    _key = chain.pop(0)
    if _key in obj:
        return exists(obj[_key], chain) if chain else obj[_key]

myDict ={
    'mainsnak': {
        'datavalue': {
            'value': {
                'numeric-id': 1
            }
        }
    }
}

result = exists(myDict, ['mainsnak', 'datavalue', 'value', 'numeric-id'])
print(result)
>>> 1

可以将.get与默认值一起使用:

s.get('mainsnak', {}).get('datavalue', {}).get('value', {}).get('numeric-id')

但这几乎肯定不如使用try/except清晰

相关问题 更多 >