链式、嵌套的dict()获取调用在Python中
我正在使用字典的 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 个回答
用一个小的辅助函数怎么样?
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]
另一种方法是,如果找不到这个键,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
。你可以分别处理这些错误,也可以一起处理。选择最适合你情况的方法。
因为这些都是 Python 的 dict
(字典),而你在对它们使用 dict.get()
方法,所以你可以用一个空的 dict
来连接这些操作:
[m.get("gparents", {}).get("parent", {}).get("child") for m in M]
如果你在最后一个 .get()
中不指定默认值,那么它会返回 None
。这样一来,如果中间的某个键找不到,后面的查找就会用空字典来继续查找,最终 .get('child')
会返回 None
。