您忽略了递归调用产生的生成器对象:
for key, item in data.items():
boil_down_array(key, item) # creates a generator object
所以递归调用并没有实际执行(生成器中的代码永远不会为该调用执行)。
您想使用 yield from 到 delegate iteration 来调用:
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}
请注意,您的代码实际上不会生成字典作为输出。它产生一个 iterable 的单键值字典:
>>> 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)) # first resulting key-value pair
('c', 'd')
>>> dict(boil_down_nested(d)) # all key-value pairs into a dictionary.
{'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) # process this value next
else:
output[key] = value
return output
不再需要单独的dict() 呼叫:
>>> boil_down_nested_dict(d)
{'c': 'd', 'e': 'f'}