我想计算JSON结构中的叶子数(即只有那些没有其他子元素的键)。
我找不到任何显而易见的事情,所以一直在尝试编写一个函数,但我很难找到一个有效的函数,而不使用全局变量。
这是我到目前为止所做的:
def count_leafs(nested):
is isinstance(nested, Mapping):
for k, v in nested.items():
if isinstance(v, Mapping):
for i_k, i_v in count_leafs(v):
yield i_k, i_v
elif isinstance(v, MutableSequence):
for i_k in v:
for i_i_k, i_i_v in i_k.items():
count_leafs(i_i_v)
else:
yield k, v
elif isinstance(nested, MutableSequence):
for k in nested:
count_leafs(k)
for k,v in count_leafs(json):
leaf_count += 1
由于计算了一些非叶子节点,因此它并没有真正起作用,并且它不会一直递归到某些结构中。
答案 0 :(得分:2)
您的伪代码过于复杂且错误。我还建议您编写紧跟PEP 8 - Style Guide for Python Code之后的代码,以供您自己和其他人阅读您编写的代码。
无论如何,作为测试用例,假设你有一些像这样的JSON数据:
json_data = {
"glossary": {
"title": "example glossary",
"answer": 42,
"boolean": True,
"nada": None,
"GlossDiv": {
"GlossList": {
"GlossEntry": {
"GlossDef": {
"GlossSeeAlso": [
"GML",
"XML"
],
"para": "A meta-markup language, used to create markup "
"languages such as DocBook."
},
"GlossSee": "markup",
"Acronym": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"SortAs": "SGML",
"Abbrev": "ISO 8879:1986",
"ID": "SGML"
}
},
"title": "S"
}
}
}
您可以递归计算叶子,如下所示:
from collections import Mapping, MutableSequence
def count_leaves(json_obj):
def leaf_iterator(json_obj):
if isinstance(json_obj, Mapping):
for v in json_obj.values():
for obj in leaf_iterator(v):
yield obj
elif isinstance(json_obj, MutableSequence):
for v in json_obj:
for obj in leaf_iterator(v):
yield obj
else:
yield json_obj
return sum(1 for leaf in leaf_iterator(json_obj))
leaf_count = count_leaves(json_data)
print('leaf count: {}'.format(leaf_count)) # -> leaf_count: 14
我在叶子计数函数中嵌套了leaf_iterator()
生成器,但如果它在更大的上下文中证明是有用的,它也可以在外面定义。通过使用Python版本3.3中引入的yield from
<expression>
,可以在Python 3中进一步简化其中的代码。
答案 1 :(得分:2)
一般来说,我更喜欢非递归解决方案而不是递归解决方案。我的算法是这样的:
以下是代码:
from collections import Mapping, Sequence, Set, deque
def count_leaves(nested):
queue = deque([nested])
count = 0
while queue:
node = queue.popleft()
if isinstance(node, Mapping):
queue.extend(node.values())
elif isinstance(node, (Sequence, Set)) and not isinstance(node, basestring):
queue.extend(node)
else:
count += 1
return count