从此 Python 生成器中删除递归
Remove recursion from this Python generator
我有一个生成器,它迭代所有键的组合,直到嵌套字典中的特定深度:
def iter_dict(levels, input_dict, items=[], sort=False, **sort_args):
for dict_key, val in (sorted(input_dict.items(), **sort_args) if
sort else input_dict.items()):
if levels == 1:
yield items + [(dict_key, val)]
else:
yield from iter_dict(levels - 1, val, items + [(dict_key, val)])
所以它的行为是这样的:
>>> d = {'a': 1, 'b': 2}
>>> list(iter_dict(1, d))
[[('a', 1)], [('b', 2)]]
和
>>> d = {'a': {'c': 1}, 'b': {'d' : 2}}
>>> list(iter_dict(1, d))
[[('a', {'c': 1})], [('b', {'d': 2})]]
>>> list(iter_dict(2, d))
[[('a', {'c': 1}), ('c', 1)], [('b', {'d': 2}), ('d', 2)]]
生成器上的每次迭代 returns 一个元组列表,第 n 个元组 (key, value)
位于嵌套字典的 n 深度。
但我在庞大的词典上实现这个功能,担心达到最大递归深度级别。
如何重写生成器以删除递归?
But I am implementing this function on huge dictionaries and am worried about reaching the maximum recursion depth level
除非您的词典实际上有 1000 层以上的嵌套,否则这应该不是问题。最大递归深度实际上仅约为 depth;分支因子不是问题。也就是说,它 可以 是个大问题 w.r.t。 运行 时间,但你不会从中得到最大递归深度错误(并且 运行 时间在没有递归的情况下是一样的)。
How can I rewrite the generator to remove the recursion?
我想这可以使用堆栈和在堆栈上存储键序列(当前子字典的所有键)来完成。这样的解决方案可能会更复杂一些,并且不如递归算法那么优雅,所以鉴于上述情况,我认为不值得付出努力。
但是无论如何,给你(稍微简化,没有排序):
from functools import reduce
def iter_dict(levels, input_dict):
def get_nested(keys):
return reduce(lambda d, k: d[k], keys, input_dict)
stack = [[k] for k in input_dict]
while stack:
keys = stack.pop()
if len(keys) == levels:
yield [(k, get_nested(keys[:i])) for i, k in enumerate(keys, 1)]
else:
stack.extend(keys + [k] for k in get_nested(keys))
示例:
>>> d = {'a': {'c': 1, "e": 2}, 'b': {'d' : 3, "f": 4}}
>>> list(iter_dict(2, d))
[[('a', {'c': 1, 'e': 2}), ('e', 2)],
[('a', {'c': 1, 'e': 2}), ('c', 1)],
[('b', {'d': 3, 'f': 4}), ('f', 4)],
[('b', {'d': 3, 'f': 4}), ('d', 3)]]
我有一个生成器,它迭代所有键的组合,直到嵌套字典中的特定深度:
def iter_dict(levels, input_dict, items=[], sort=False, **sort_args):
for dict_key, val in (sorted(input_dict.items(), **sort_args) if
sort else input_dict.items()):
if levels == 1:
yield items + [(dict_key, val)]
else:
yield from iter_dict(levels - 1, val, items + [(dict_key, val)])
所以它的行为是这样的:
>>> d = {'a': 1, 'b': 2}
>>> list(iter_dict(1, d))
[[('a', 1)], [('b', 2)]]
和
>>> d = {'a': {'c': 1}, 'b': {'d' : 2}}
>>> list(iter_dict(1, d))
[[('a', {'c': 1})], [('b', {'d': 2})]]
>>> list(iter_dict(2, d))
[[('a', {'c': 1}), ('c', 1)], [('b', {'d': 2}), ('d', 2)]]
生成器上的每次迭代 returns 一个元组列表,第 n 个元组 (key, value)
位于嵌套字典的 n 深度。
但我在庞大的词典上实现这个功能,担心达到最大递归深度级别。
如何重写生成器以删除递归?
But I am implementing this function on huge dictionaries and am worried about reaching the maximum recursion depth level
除非您的词典实际上有 1000 层以上的嵌套,否则这应该不是问题。最大递归深度实际上仅约为 depth;分支因子不是问题。也就是说,它 可以 是个大问题 w.r.t。 运行 时间,但你不会从中得到最大递归深度错误(并且 运行 时间在没有递归的情况下是一样的)。
How can I rewrite the generator to remove the recursion?
我想这可以使用堆栈和在堆栈上存储键序列(当前子字典的所有键)来完成。这样的解决方案可能会更复杂一些,并且不如递归算法那么优雅,所以鉴于上述情况,我认为不值得付出努力。
但是无论如何,给你(稍微简化,没有排序):
from functools import reduce
def iter_dict(levels, input_dict):
def get_nested(keys):
return reduce(lambda d, k: d[k], keys, input_dict)
stack = [[k] for k in input_dict]
while stack:
keys = stack.pop()
if len(keys) == levels:
yield [(k, get_nested(keys[:i])) for i, k in enumerate(keys, 1)]
else:
stack.extend(keys + [k] for k in get_nested(keys))
示例:
>>> d = {'a': {'c': 1, "e": 2}, 'b': {'d' : 3, "f": 4}}
>>> list(iter_dict(2, d))
[[('a', {'c': 1, 'e': 2}), ('e', 2)],
[('a', {'c': 1, 'e': 2}), ('c', 1)],
[('b', {'d': 3, 'f': 4}), ('f', 4)],
[('b', {'d': 3, 'f': 4}), ('d', 3)]]