问题:展开如下列表
ls1 = [1, 2, 3]
ls2 = [1, [2, 3], 4, [5, 6]]
ls3 = [1, [2, [3, 4]], 5, [6, 7, 8]]
if… else 版本:
def expandlist(nested):
for sublist in nested:
if isinstance(sublist, list):
# 可以使用 collections.abc 基类的 Iterable 来判断
for item in expandlist(sublist):
yield item
else:
yield sublist
try … except 版本
def nest_list(nested):
try:
for sublist in nested:
for item in nest_list(sublist):
yield item
except TypeError:
yield nested
代码分析
- 使用try…except… 展开嵌套列表,无需判断sublist是否为list,利用sublist 为非迭代类型触发异常yield 数据,这里是int 类型。
- if…else 版本判断sublist 是否是列表,决定是否调用递归。
- 结束递归条件都是数据无法再拆解则利用异常或条件判断yield 数据。
- 主程序中调用该生成器函数得到的数据是生成器里的第1层循环(非递归)传回的数据,递归嵌套中yield 返回的数据插入到上层递归的生成器中。所以代码可以看做是利用生成器的递归使用解包嵌套列表。
for item in nest_list(sublist):
yield item
- 递归结束条件:
try… except
except TypeError:
yield nested
if … else
if isinstance(sublist, list):
for item in expandlist(sublist):
yield item
else:
yield sublist
调试过程:
第1层递归
第2层递归
第3层递归
完整代码:
# coding:utf-8
from collections.abc import Iterable
def nest_list(nested):
try:
for sublist in nested:
for item in nest_list(sublist):
yield item
except TypeError:
yield nested
def expandlist(nested):
for sublist in nested:
if isinstance(sublist, list):
for item in expandlist(sublist):
yield item
else:
yield sublist
if __name__ == '__main__':
ls1 = [1, 2, 3]
ls2 = [1, [2, 3], 4, [5, 6]]
ls3 = [1, [2, [3, 4]], 5, [6, 7, 8]]
for n in expandlist(ls3):
print(n, end='\t')
总结:
使用递归展开嵌套列表,实际上是使用了生成器的嵌套。每一层递归都返回了一个生成器。yield 语句更像是一个带断点的return语句,当迭代器中还有数据时,会从上次的断点处执行。否则使用return语句会从头开始执行,造成数据重复。