您忽略了递归调用产生的生成器对象:
for key, item in data.items():
boil_down_array(key, item) # creates a generator object
所以递归调用实际上并没有执行(生成器中的代码在该调用中从未执行)。
你需要使用
yield from
来
委托迭代给该调用:
for key, item in data.items():
yield from boil_down_array(key, item)
"
yield from
将控制权从当前生成器移交到yield from
后面表达式所产生的迭代器;在这里就是你的递归生成器。
yield from
要求使用Python 3.3或更新版本。如果你正在使用Python 2或更早版本的Python 3,你也可以添加另一个循环,显式地产生每个迭代产生的结果:
"
for key, item in data.items():
for result in boil_down_array(key, item):
yield result
我也会使用 isinstance(data, dict)
而不是使用 type(...) ==
,以允许子类存在:
def boil_down_array(key, data):
if isinstance(data, dict):
for key, item in data.items():
yield from boil_down_array(key, item)
else:
yield {key: data}
请注意,您的代码实际上并不会产生一个字典作为输出。它会产生一个由单个键值字典组成的可迭代对象:
>>> d = {'a': {'b': {'c': 'd', 'e': 'f'}}}
>>> list(boil_down_array('v', d))
[{'c': 'd'}, {'e': 'f'}]
在这里,最外层调用中的
key
参数也是多余的,因为你将其替换为当前迭代的键。
如果你确实需要坚持使用生成器函数,那么至少要产生
(key, value)
元组,并且当
value不是字典时不必递归(所以在递归之前进行测试),以消除传递键的需要;剩下的
data
参数现在始终被假定为一个字典:
def boil_down_nested(data):
for key, value in data.items():
if isinstance(value, dict):
yield from boil_down_nested(value)
else:
yield (key, value)
使用dict(boil_down_nested(input_dict))
从生成器现在输出的键值元组中产生一个新字典:
>>> next(boil_down_nested(d))
('c', 'd')
>>> dict(boil_down_nested(d))
{'c': 'd', 'e': 'f'}
没有递归,您可以使用堆栈来跟踪仍需处理的嵌套字典;这使得直接输出字典作为结果变得更加容易:
def boil_down_nested_dict(d):
stack = [d]
output = {}
while stack:
for key, value in stack.pop().items():
if isinstance(value, dict):
stack.append(value)
else:
output[key] = value
return output
不再需要单独的
dict()
调用:
>>> boil_down_nested_dict(d)
{'c': 'd', 'e': 'f'}