yield 的理解(2) 顺便 探索下for 循环 ,以及递归
代码目的:
利用递归,获得一个List中所有的元素
同时说明一下for ,以及 yield的特点。
代码:
net = [[[1,2],3],4,[5,6]] def flatten(nested = net ,level = 0): level +=1 count = 0 try: for sublist in nested: count += 1 print ('level {} sublist {} is {} '.format(level,count ,sublist) ) for element in flatten(sublist,level): print('back to level:{}'.format(level)) yield element except TypeError: print('fined node:{}'.format(nested)) yield nested
解释一下代码:
net
是要被分解的列表。
flatten(nested ,level ) :
递归调用的函数,用于一层一层的进入嵌套列表,直到找到数据元素
当flatten 的参数nested ,被赋值为数据元素(而不是列表时) ,for sublist in nested 会触发 错误: “TypeError”
此时进入except TypeError ,Yield nested 会暂停该函数,返回到上一级函数去继续执行。
注意:Yield并不代表整个程序暂停,而仅是Yield所在函数暂停,并返回值。
上一个函数此时正在 for element in flatten(sublist,level) ,于是element = 找到的节点,并借由yield element ,一层一层往外退。
当退到shell 层,代码暂停。用户需要继续输入 next(g) ,来调用下一次查找.
注意,next(g)后,程序是从递归最内层的 yield开始继续执行的。
第一次执行next(g),以及第二次执行next(g)的入口,流程如下图(为了节约空间,去掉了print()):
这个图说明了两点:
1. yield是向上一层返回值,和return的路径一样。
2. next(g),是从最早的一次yield处回复的。
可以理解为,当多个yield依次调用时,yield的现场被放入一个“栈”。 使用next恢复时,从栈的最底端保存的yield现场,恢复执行.
疑问:栈中的其他yield ,是否就丢弃了,暂时没有答案.
实验:
在python中执行之前的代码:
net = [[[1,2],3],4,[5,6]] def flatten(nested = net ,level = 0): level +=1 count = 0 try: for sublist in nested: count += 1 print ('level {} sublist {} is {} '.format(level,count ,sublist) ) for element in flatten(sublist,level): print('back to level:{}'.format(level)) yield element except TypeError: print('fined node:{}'.format(nested)) yield nested
执行过程,和结果如下:
>>> g= flatten() >>> next(g) level 1 sublist 1 is [[1, 2], 3] level 2 sublist 1 is [1, 2] level 3 sublist 1 is 1 fined node:1 back to level:3 back to level:2 back to level:1 1 >>> next(g) level 3 sublist 2 is 2 fined node:2 back to level:3 back to level:2 back to level:1 2 >>> next(g) level 2 sublist 2 is 3 fined node:3 back to level:2 back to level:1 3 >>> next(g) level 1 sublist 2 is 4 fined node:4 back to level:1 4 >>> next(g) level 1 sublist 3 is [5, 6] level 2 sublist 1 is 5 fined node:5 back to level:2 back to level:1 5 >>> next(g) level 2 sublist 2 is 6 fined node:6 back to level:2 back to level:1 6 >>> next(g) Traceback (most recent call last): File "<pyshell#21>", line 1, in <module> next(g) StopIteration >>>
可见,列表分层如下:
然后,还要提到对 python的 for 语句的理解。
首先,在C语言中, 简单的一句 for( i =0 ; i < 10 ; i ++ ) { do something} 是这样执行的:
1.更具语句1“ i= 0 ” 获得初值
2. 判断语句2" i<10 " 是否为真,不为真,则退出for 循环,执行下一个程序块。
3. 如果第二条判断为真,那么首先执行了循环体,其后执行 第三条 "i++"
4. 回到2.
然后, python 中 for语句是不同的。
我理解的(for x in y )中,y是一个可迭代的对象,而不是一个条件判断语句或者范围值。
即是说,可以多次访问y ,每次访问后,y都会迭代,发生变化,并可输出空(迭代结束)。
比如 ,当y 是一个 list对象时,循环处理如下:
1. y产生一次迭代,如为空,则跳出循环。 如值有效,则把值赋给x
2. 执行循环体
3. 回到1.
那么,回到递归这句话:
for element in flatten(sublist,level)
由于flatten 函数中具有yield, 这让他成为一个生成器 ,而生成器的特点是,当他第一次被调用时,将返回一个迭代器。
所以,此时for 语句中,flatten就是一个迭代器,每次调用的后,函数的返回值,就是要赋给x的值。