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

loo*_*omi 53 python variables object

是否有更可读的方法来检查埋在dict中的密钥是否存在而不是单独检查每个级别?

假设我需要在埋藏的对象中获取此值(示例来自维基数据):

x = s['mainsnak']['datavalue']['value']['numeric-id']
Run Code Online (Sandbox Code Playgroud)

为了确保这不会以运行时错误结束,有必要检查每个级别,如下所示:

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']
Run Code Online (Sandbox Code Playgroud)

我能想到解决这个问题的另一种方法是将它包装成一个try catch构造,我觉得这个简单的任务也很尴尬.

我正在寻找类似的东西:

x = exists(s['mainsnak']['datavalue']['value']['numeric-id'])
Run Code Online (Sandbox Code Playgroud)

True如果所有级别都存在则返回

Aro*_*unt 90

简而言之,使用Python,您必须相信,请求宽恕比获得更容易

try:
    x = s['mainsnak']['datavalue']['value']['numeric-id']
except KeyError:
    pass
Run Code Online (Sandbox Code Playgroud)

答案

这是我如何处理嵌套的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
Run Code Online (Sandbox Code Playgroud)

例:

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"))
Run Code Online (Sandbox Code Playgroud)

输出:

spam (exists): True
spam > bacon (do not exists): False
spam > egg (exists): True
spam > egg > bacon (exists): True
Run Code Online (Sandbox Code Playgroud)

它以给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)
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,但有一件事应该改变:“if type(element) is not dict”改为“if not isinstance(element, dict)”。这样它也适用于像 OrderedDict 这样的类型。 (3认同)

Dan*_*man 11

您可以使用.get默认值:

s.get('mainsnak', {}).get('datavalue', {}).get('value', {}).get('numeric-id')
Run Code Online (Sandbox Code Playgroud)

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

  • 我经常使用这个结构,但被它击中了脚。使用上面的示例时要小心,因为如果“getted”元素实际存在并且不是 dict (或可以调用“get”的对象)(我的情况是 None ),则最终会得到“NoneType”对象没有属性“get”或任何类型。 (7认同)

Luc*_*uez 8

蟒蛇 3.8 +

dictionary = {
    "main_key": {
        "sub_key": "value",
    },
}

if sub_key_value := dictionary.get("main_key", {}).get("sub_key"):
    print(f"The key 'sub_key' exists in dictionary[main_key] and it's value is {sub_key_value}")
else:
    print("Key 'sub_key' doesn't exists or their value is Falsy")
Run Code Online (Sandbox Code Playgroud)

额外的

一点但重要的澄清。

在前面的代码块中,我们验证字典中存在一个键,但它的值也是Truthy。大多数时候,这才是人们真正想要的,我认为这也是 OP 真正想要的。然而,这并不是最“正确”的答案,因为如果键存在但它的值为 False,上面的代码块会告诉我们键不存在,这是不正确的。

所以,我在这里给出一个更正确的答案:

dictionary = {
    "main_key": {
        "sub_key": False,
    },
}

if "sub_key" in dictionary.get("main_key", {}):
    print(f"The key 'sub_key' exists in dictionary[main_key] and it's value is {dictionary['main_key']['sub_key']}")
else:
    print("Key 'sub_key' doesn't exists")
Run Code Online (Sandbox Code Playgroud)


Mau*_*yer 7

尝试/例外似乎是最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
Run Code Online (Sandbox Code Playgroud)


Ale*_*der 6

您可以使用pydash检查是否存在:http : //pydash.readthedocs.io/en/latest/api.html#pydash.objects.has

或获取值(您甚至可以设置默认值 - 如果不存在则返回):http : //pydash.readthedocs.io/en/latest/api.html#pydash.objects.has

下面是一个例子:

>>> get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c[1]')
2
Run Code Online (Sandbox Code Playgroud)


Fab*_*amo 6

我建议您使用python-benedict,一个具有完整键路径支持和许多实用方法的可靠 python dict 子类。

你只需要投射你现有的字典:

s = benedict(s)
Run Code Online (Sandbox Code Playgroud)

现在您的 dict 具有完整的 keypath 支持,您可以使用 in 运算符检查密钥是否以 pythonic 方式存在:

if 'mainsnak.datavalue.value.numeric-id' in s:
    # do stuff
Run Code Online (Sandbox Code Playgroud)

这里是库存储库和文档:https : //github.com/fabiocaccamo/python-benedict

注意:我是这个项目的作者