使用递归函数返回一个新字典:
def clean_empty(d):
if isinstance(d, dict):
return {
k: v
for k, v in ((k, clean_empty(v)) for k, v in d.items())
if v
}
if isinstance(d, list):
return [v for v in map(clean_empty, d) if v]
return d
{..}
构造是一个字典推导式;它仅包含原始字典中
v
为真的键,例如不为空。类似地,
[..]
构造生成一个列表。
嵌套的(.. for ..)
构造是一种生成器表达式,允许代码在递归之后紧凑地过滤空对象。
构建这样一个函数的另一种方法是使用@singledispatch
装饰器;然后您编写多个函数,每个函数针对一个对象类型:
from functools import singledispatch
@singledispatch
def clean_empty(obj):
return obj
@clean_empty.register
def _dicts(d: dict):
items = ((k, clean_empty(v)) for k, v in d.items())
return {k: v for k, v in items if v}
@clean_empty.register
def _lists(l: list):
items = map(clean_empty, l)
return [v for v in items if v]
上述的@singledispatch
版本与第一个函数完全相同,但现在基于注册函数的类型注释,由装饰器实现处理isinstance()
测试。我还将嵌套迭代器(生成器表达式和map()
函数)放入单独的变量中,以进一步提高可读性。
请注意,任何设置为数字0(整数0、浮点数0.0)的值也将被清除。您可以使用if v or v == 0
保留数字0值。
第一个函数的演示:
>>> sample = {
... "fruit": [
... {"apple": 1},
... {"banana": None}
... ],
... "veg": [],
... "result": {
... "apple": 1,
... "banana": None
... }
... }
>>> def clean_empty(d):
... if isinstance(d, dict):
... return {
... k: v
... for k, v in ((k, clean_empty(v)) for k, v in d.items())
... if v
... }
... if isinstance(d, list):
... return [v for v in map(clean_empty, d) if v]
... return d
...
>>> clean_empty(sample)
{'fruit': [{'apple': 1}], 'result': {'apple': 1}}