Python:使用列表安全访问字典?



是否存在从包含列表的字典中访问值的异常free方式?例如,如果我有:

data = {
    "object_1": {
        "object_2": {
            "list": [
                {
                    "property": "hello"
                }
            ]
        }
    }
}

我如何安全地访问路径data['object_1']['object_2']['list'][0]['property'](即返回一些默认值,如果不可能访问而不抛出错误)?我试图避免在try-except中包装这些。我已经看到了基于reduce的方法,但它没有考虑到在字典中有列表。

在JS中,我可以这样写:
data.object_1?.object_2?.list[0]?.property ?? 'nothing_found'

在Python中有类似的东西吗?

对于dict,可以使用get方法。对于列表,只需小心索引:

data.get('object_1', {}).get('object_2', {}).get('list', [{}])[0].get('property', default)

这有点尴尬,因为它为每次调用get创建一个新的临时字典或丢失。对于列表来说,它也不是超级安全的,因为列表没有相应的方法。

你也可以将getter封装在一个小例程中来支持列表,但是这样做并不值得。您最好编写一个一次性实用程序函数,该函数使用异常处理或初步检查来处理您想要响应的情况:

def get(obj, *keys, default=None):
    for key in keys:
        try:
            obj = obj[key]
        except KeyError, IndexError:
            return default
    return obj
与其他方式相比,异常处理有几个巨大的优点。首先,您不必根据对象是字典还是列表对键进行单独检查。另一方面,您可以支持几乎任何其他合理的支持__getitem__索引的类型。为了表达我的意思,下面是请求允许而不是原谅的方法:
from collections.abc import Mapping, Sequence
from operator import index
def get(obj, *keys, default=None):
    for key in keys:
        if isinstance(obj, Mapping):
            if key not in obj:
                return default
        elif isinstance(obj, Sequence):
            try:
                idx = index(key)
            except TypeError:
                return default
            if len(obj) <= idx or len(obj) < -idx:
                 return default
        obj = obj[key]
    return obj

观察检查是多么尴尬和容易出错。尝试传入一个自定义对象,而不是一个list,或者一个不是整数的键。在Python中,小心使用的异常是你的朋友,这是Python要求原谅而不是允许的原因。

啊哈。是的,访问JSON数据结构很糟糕,这有点尴尬。

快来救我!

有两种方法可以赢:

  1. 您可以直接指定... , default=None)以避免异常,…或…

  2. 使用Coalesce .

     print(glom(data, {'object_1.object_2.list': ['property']}, default=None))
    

在下面的代码中,如果'object_1'/'object_2'/'list'键不存在,x将返回None

同样,如果我们能够访问'list' key,那么我们将x设置为非None,并且我们应该确保列表的长度应该大于零,然后我们可以搜索'property' key。

x = data.get('object_1', {}).get('object_2', {}).get('list')
    
if x is not None and len(x) > 0:
        print(x[0].get('property'))
else:
        print(None)

有一种方法可以做到这一点,但它将涉及get方法,并且将涉及大量检查或使用临时值。

查找函数的一个示例如下:

def lookup(data):
    object_1 = data.get("object_1")
    if object_1 is None:
        # return your default
    object_2 = object_1.get('object_2')
    # and so on...

在Python 3.10及以上版本中,也有结构模式匹配可以提供帮助,在这种情况下,您可以这样做:

match data:
    case {'object_1': {'object_2': {'list': [{'property': x}]}}}:
        print(x) # should print 'hello'
    case _:
        print(<your_default>)

请记住,这只适用于最新版本的Python (Python.org上的在线Python控制台仍然只在Python3.9上,上面的代码会导致语法错误)。

相关内容

  • 没有找到相关文章

最新更新