所以我有一本字典:
{'a': {'b': {'c': 'd', 'e': 'f'}}}
我需要创建一个字典,如下所示:
{'c':'d', 'e','f'}
它可以更深入到任何级别,但我应该始终以最大深度获得键值对。所以我写了一个函数:
def boil_down_array(key, data):
if type(data) == dict:
for key, item in data.items():
boil_down_array(key, item)
else:
yield {key:data}
现在的问题是,一旦递归,yield
就丢失了。我如何再次产生该词典?我所得到的只是一个发电机,这不是我想要的。
答案 0 :(得分:5)
在递归调用中使用yield from
,否则您将忽略递归调用的结果:
def boil_down_array(key, data):
if type(data) == dict:
for key, item in data.items():
yield from boil_down_array(key, item)
else:
yield {key: data}
这仅在Python> 3.3中可用,但本质上只是简单地从额外循环中产生的简捷方式:
for key, item in data.items():
for x in boil_down_array(key, item): # just exhaust the recursive generator
yield x # and "re-yield" what it produces
为了获得所需的数据结构,最好放弃对,而不是dicts
,这样就可以更轻松地将结果转换为结果dict
:
yield key, data
然后您可以像使用它一样
result = dict(boil_down_array(None, input_dict))
一种更简单的递归方法将只返回完整的dict
:
def boil_down_nested(dct):
result = {}
for k, v in dct.items():
if isinstance(v, dict):
result.update(boil_down_nested(v))
else:
result[k] = v
return result
答案 1 :(得分:4)
您将忽略递归调用生成的生成器对象:
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'}
答案 2 :(得分:0)
请注意,您不一定需要使用yield
:
def last(d):
c = [i for b in d.items() for i in ([b] if not isinstance(b[-1], dict) else last(b[-1]))]
return c
print(dict(last({'a': {'b': {'c': 'd', 'e': 'f'}}})))
输出:
{'c': 'd', 'e': 'f'}