链式、嵌套的dict()获取调用在Python中

40 投票
4 回答
27897 浏览
提问于 2025-04-17 13:25

我正在使用字典的 dict.get('keyword') 方法来查询一个嵌套的字典。现在我的写法是...

M = cursor_object_results_of_db_query

for m in M:
    X = m.get("gparents").get("parent").get("child")
    for x in X:
        y = x.get("key")

不过,有时候“父”标签或“子”标签可能不存在,这样我的程序就会出错。我知道使用 get() 方法时,可以设置一个默认值,以防这个键不存在,写法是...

get("parent", '') or
get("parent", 'orphan') 

但是如果我设置了 Null'' 或者其他任何空值,调用 ''.get("child") 时,链式调用 .get("child") 就会失败,因为 "" 是没有 .get() 方法的。

现在我解决这个问题的方法是,在每个 .get("") 调用周围使用一堆顺序的 try-except,但这样看起来很傻,而且不符合 Python 的风格——有没有什么方法可以默认返回 "skip""pass",或者其他什么东西,这样还能支持链式调用,并且在出错时能聪明地处理,而不是深入到那些不存在的键里去呢?

理想情况下,我希望能用一种列表推导的方式来实现,形式是:

[m.get("gparents").get("parent").get("child") for m in M]

但现在如果父键缺失,就会导致 .get("child") 的调用终止我的程序,这样是无法实现的。

4 个回答

6

用一个小的辅助函数怎么样?

def getn(d, path):
    for p in path:
        if p not in d:
            return None
        d = d[p]
    return d

然后

[getn(m, ["gparents", "parent", "child"]) for m in M]
10

另一种方法是,如果找不到这个键,dict.get 会返回 None。但是,None 是没有 .get 这个属性的,所以会抛出一个 AttributeError 错误:

for m in M:
    try:
       X = m.get("gparents").get("parent").get("child")
    except AttributeError:
       continue

    for x in X:
        y = x.get("key")
        #do something with `y` probably???

就像 Martijn 的回答一样,这并不能保证 X 是可迭代的(不是 None)。不过,你可以通过让链条最后一个 get 默认返回一个空列表来解决这个问题:

 try:
    X = m.get("gparents").get("parent").get("child",[])
 except AttributeError:
    continue

最后,我认为解决这个问题的最好方法可能是使用 reduce

try:
    X = reduce(dict.__getitem__,["gparents","parent","child"],m)
except (KeyError,TypeError):
    pass
else:
    for x in X:
       #do something with x

这样做的好处是,你可以根据抛出的异常类型知道哪个 get 失败了。如果某个 get 返回了错误的类型,你会得到一个 TypeError 错误。如果字典里没有这个键,它会抛出一个 KeyError。你可以分别处理这些错误,也可以一起处理。选择最适合你情况的方法。

107

因为这些都是 Python 的 dict(字典),而你在对它们使用 dict.get() 方法,所以你可以用一个空的 dict 来连接这些操作:

[m.get("gparents", {}).get("parent", {}).get("child") for m in M]

如果你在最后一个 .get() 中不指定默认值,那么它会返回 None。这样一来,如果中间的某个键找不到,后面的查找就会用空字典来继续查找,最终 .get('child') 会返回 None

撰写回答