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

Python 检查dict中是否存在嵌套键的优雅方法?,python,variables,object,Python,Variables,Object,是否有更可读的方法来检查dict中是否存在隐藏的密钥,而不单独检查每个级别 假设我需要在一个隐藏的对象中获取这个值(例如从Wikidata中获取): 为了确保这不会以运行时错误结束,有必要像这样检查每个级别: if 'mainsnak' in s and 'datavalue' in s['mainsnak'] and 'value' in s['mainsnak']['datavalue'] and 'nurmeric-id' in s['mainsnak']['datavalue']['va

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

假设我需要在一个隐藏的对象中获取这个值(例如从Wikidata中获取):

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

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'])
expected_keys = ['spam', 'egg', 'bacon']
keys_exists(data, *expected_keys)

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

简而言之,对于Python,您必须信任它


答案 下面是我如何处理嵌套的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
它在给定的
元素中循环
以给定的顺序测试每个键

与我找到的所有
variable.get('key',{})
方法相比,我更喜欢这个方法,因为它如下所示

除了被调用之外的函数如下:
keys\u exists(dict\u element\u to\u test,'key\u level\u 0','key\u level\u 1','key\u level\u n',…)
。至少需要两个参数,元素和一个键,但可以添加所需的键数

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

x = exists(s['mainsnak']['datavalue']['value']['numeric-id'])
expected_keys = ['spam', 'egg', 'bacon']
keys_exists(data, *expected_keys)

您可以使用带有默认值的
.get

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

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

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


您可以使用
pydash
检查是否存在:

或者获取值(如果不存在,甚至可以将default设置为return):

以下是一个例子:

>>> get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c[1]')
2

我编写了一个数据解析库,用于此类情况,基本上是因为我对Wikidata API返回的JSON感到失望

有了这个图书馆,你可以做这样的事情

from dataknead import Knead

numid = Knead(s).query("mainsnak/datavalue/value/numeric-id").data()

if numid:
    # Do something with `numeric-id`

尝试/例外的方式是最干净的,没有竞争。但是,它在我的IDE中也算作异常,在调试时会停止执行

此外,我不喜欢在方法控制语句中使用异常,这本质上就是try/catch所发生的情况

下面是一个不使用递归并支持默认值的简短解决方案:

def chained_dict_lookup(lookup_dict, keys, default=None):
    _current_level = lookup_dict
    for key in keys:
        if key in _current_level:
            _current_level = _current_level[key]
        else:
            return default
    return _current_level

如果您可能需要测试对象路径的字符串表示形式,则此方法可能适用于您:

def exists(str):
    try:
        eval(str)
        return True
    except:
        return False

exists("lst['sublist']['item']")

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

您只需转换现有的命令:

s = benedict(s)
现在,您的dict具有完整的密钥路径支持,您可以使用in运算符检查密钥是否以pythonic方式存在:

if 'mainsnak.datavalue.value.numeric-id' in s:
    # do stuff
这里是库存储库和文档:


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

我遇到了同样的问题,最近出现了python库:

因此,在你的情况下:

from dictor import dictor

x = dictor(s, 'mainsnak.datavalue.value.numeric-id')

个人提示:
我不喜欢“dictor”这个名字,因为它并没有暗示它的实际功能。所以我使用它就像:

from dictor import dictor as extract
x = extract(s, 'mainsnak.datavalue.value.numeric-id')
无法想出比
提取
更好的命名方法。如果您提出了更可行的命名方法,请随时发表评论
safe\u-get
robust\u-get
不适合我的情况。

Python3.8+
字典={
“主键”:{
“子密钥”:“值”,
},
}
如果sub_key_value:=dictionary.get(“main_key”,{}).get(“sub_key”):
打印(f“字典[main_key]中存在键'sub_key',其值为{sub_key_value}”)
其他:
打印(“密钥“子密钥”不存在)
另一种方式:

def does_nested_key_exists(dictionary, nested_key):
    exists = nested_key in dictionary
    if not exists:
        for key, value in dictionary.items():
            if isinstance(value, dict):
                exists = exists or does_nested_key_exists(value, nested_key)
    return exists
被接受的方法是好的,但这里有另一种方法。在我看来,如果你经常这样做的话,打字会少一点,眼睛也会容易一点。它也不需要任何额外的包依赖项,就像其他一些答案一样。没有比较过表现

导入工具
def haskey(d,路径):
尝试:
functools.reduce(λx,y:x[y],path.split(“.”),d)
返回真值
除KeyError外:
返回错误
#将这种方法用于嵌套get,真是见鬼。。。
def getkey(d,路径,*默认值):
尝试:
返回functools.reduce(lambda x,y:x[y],path.split(“.”),d)
除KeyError外:
如果默认:
返回默认值[0]
提升
用法:

数据={
“垃圾邮件”:{
“鸡蛋”:{
“培根”:“嗯…”,
“香肠”:“垃圾鸡蛋香肠和垃圾”,
“垃圾邮件”:“里面没有太多垃圾邮件”,
}
}
}
(Pdb)haskey(数据,“垃圾邮件”)
真的
(Pdb)haskey(数据,“spamw”)
假的
(Pdb)haskey(数据,“spam.egg”)
真的
(Pdb)haskey(数据,“spam.egg3”)
假的
(Pdb)haskey(数据,“spam.egg.bacon”)
真的

问题答案的原始灵感。

是的,如前所述,这是一个有效的解决方案。但是,假设一个函数访问这样一个变量的10倍,那么所有的
try-except
语句都会留下一个很大的膨胀。@lomi您可以用这个
try-except
逻辑来创建一个小函数,并简单地调用每个函数time@loomi将其包装在函数中。“用两个词来说,用Python你必须相信,请求原谅要比请求允许容易得多”用的不仅仅是两个词。答案很好,但有一点应该改变:
if type(element)not dict
改为
if not isinstance(element,dict)
。这种方式也适用于OrderedDict等类型。无论您将最后一个
get
作为默认值,它可能恰好是
s['mainsnak']['datavalue']['value']['numeric-id']的值
。我一直在使用这个构造,刚刚被它击中了脚。在使用上面的示例时要小心,因为如果“getted”元素实际上存在并且不是dict(或者可以调用
get的对象)
(我的情况下没有),那么这将导致
“NoneType”对象没有属性“get”
def does_nested_key_exists(dictionary, nested_key):
    exists = nested_key in dictionary
    if not exists:
        for key, value in dictionary.items():
            if isinstance(value, dict):
                exists = exists or does_nested_key_exists(value, nested_key)
    return exists