带索引路径的任意深度嵌套列表扁平化迭代实现问询
迭代式实现深度优先扁平化迭代器(带索引路径)
嘿,这个问题我之前也琢磨过!递归生成器虽然写起来快,但碰到极深的嵌套时容易触发RecursionError,而且有时候就是想搞个纯迭代的实现。其实用Python标准库(甚至不用额外导入itertools都能行)完全能搞定,核心思路就是用**栈(stack)**模拟递归过程,全程不用递归,还能灵活适配各种嵌套场景~
核心实现思路
深度优先遍历的本质是「先处理子节点,再处理兄弟节点」,我们可以用栈来保存待处理的迭代上下文:每个栈元素包含「当前要遍历的可迭代对象的迭代器」和「当前节点的父索引路径」。每次从栈顶取出元素处理,遇到子列表时,先把当前迭代器的剩余部分放回栈(等子列表处理完再回来处理),再把子列表的迭代器和新路径压入栈,以此实现深度优先的顺序。
代码实现
这里我们还做了一个常见的优化:跳过字符串/字节串的拆分(因为它们虽然是可迭代对象,但通常我们不想把"abc"拆成['a','b','c']),如果需要拆分字符串,去掉对应判断即可。
from collections.abc import Iterable def flatten_with_path(nested_collection): # 栈元素格式:(当前可迭代对象的迭代器, 父节点的索引路径) stack = [(iter(nested_collection), [])] while stack: current_iter, parent_path = stack.pop() for idx, item in enumerate(current_iter): # 判断是否是需要继续展开的嵌套可迭代对象 if isinstance(item, Iterable) and not isinstance(item, (str, bytes)): # 把当前迭代器的剩余部分放回栈(后续处理兄弟节点) stack.append((current_iter, parent_path)) # 把子可迭代对象和新路径压入栈(优先处理子节点) stack.append((iter(item), parent_path + [idx])) break # 跳出循环,立即处理刚压入的子节点 else: # 叶子节点,生成(元素, 索引路径)对 yield (item, parent_path + [idx])
测试用例
我们用一个典型的嵌套列表测试:
nested_example = [1, [2, 3, [4, 5]], 6, ["hello", [7, 8]]] for elem, path in flatten_with_path(nested_example): print(f"元素: {elem}, 索引路径: {path}")
输出结果(完全符合深度优先顺序):
元素: 1, 索引路径: [0] 元素: 2, 索引路径: [1, 0] 元素: 3, 索引路径: [1, 1] 元素: 4, 索引路径: [1, 2, 0] 元素: 5, 索引路径: [1, 2, 1] 元素: 6, 索引路径: [2] 元素: hello, 索引路径: [3, 0] 元素: 7, 索引路径: [3, 1, 0] 元素: 8, 索引路径: [3, 1, 1]
为什么这个实现更优?
- 无递归深度限制:不管嵌套多深,都不会触发
RecursionError,适合处理极端场景; - 内存效率高:用迭代器而非完整列表存储待处理内容,不会一次性加载所有元素到内存;
- 通用性强:支持所有实现了
Iterable接口的对象(不止列表),比如元组、集合等,只要调整判断逻辑就能适配更多场景; - 纯标准库:只用到了内置的
Iterable抽象和栈结构,不需要额外依赖。
如果一定要结合itertools的话,其实可以用itertools.chain来合并迭代器,但上面的实现已经足够简洁高效,没必要画蛇添足啦~
内容的提问来源于stack exchange,提问作者RBF06
相关产品推荐
相关产品推荐

