是否存在从包含列表的字典中访问值的异常free方式?例如,如果我有:
data = {
"object_1": {
"object_2": {
"list": [
{
"property": "hello"
}
]
}
}
}
我如何安全地访问路径data['object_1']['object_2']['list'][0]['property']
(即返回一些默认值,如果不可能访问而不抛出错误)?我试图避免在try-except
中包装这些。我已经看到了基于reduce的方法,但它没有考虑到在字典中有列表。
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数据结构很糟糕,这有点尴尬。
快来救我!
有两种方法可以赢:
-
您可以直接指定
... , default=None)
以避免异常,…或… -
使用
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上,上面的代码会导致语法错误)。