Python生成器
def countDown(i):
if i<> 0:
For j in countDown( i - 1 ):
yield j
yield i
if __name__ == '__main__':
for k in countDown(5):
print k
if k == 6 : break
在调试这段代码时,执行的流程和我预想的有点不同。
我的预想是:在countDown函数中的“for”循环会递归调用这个函数:countDown(4)、countDown(3)、countDown(2)、countDown(1)和countDown(0)。到这时,如果if条件不满足,就会开始“返回阶段”,执行Yield j。这会把控制权返回到“主”函数中的for循环。
接下来,这个for循环又会调用countDown(5),从上一个Yield停止的地方开始 - Yield j。这会执行for ... countdown(i-1)循环,并调用countDown(4)、countDown(3)、countDown(2)... countDown(0)。然后“返回阶段”开始,并触及第一个Yield j(此时j已经在第一次循环中增加了)。这会把控制权返回到“主”函数中的for循环。
在调试中,我发现了以下几点:
1) 一旦在递归结束时if i<>0失败,似乎会执行Yield i。然后它似乎会弹出栈中的每个countDown,控制权才会返回到for k ... main。
2) 在第二次迭代中,for k in g,栈进入返回阶段 - 栈顶到底部是countDown(1)、countDown(2)、countDown(3)、countDown(4),然后开始“返回阶段”。在这个阶段,j的值是'1'。那么,为什么在开始返回阶段之前不去countDown(0)那里i=1呢?
有没有人能解释一下这个是如何工作的 - 递归和生成器函数的结合?抱歉发了这么长的帖子 - 没有其他方式来解释。
谢谢大家的回复。
到目前为止我的理解:
1) for k in countDown(5)的第一次迭代:
函数 指令
======== ===========
countdown(5) for j in countDown(4)
countdown(4) for j in countDown(3)
countDown(3) for j in countDown(2)
countDown(2) for j in countDown(1)
countDown(1) for j in countDown(0)
countDown(0) 如果条件失败,返回空值
返回阶段
a)
countdown(5) for j in countDown(4)
countdown(4) for j in countDown(3)
countDown(3) for j in countDown(2)
countDown(2) for j in countDown(1)
CountDown(1) for j in countDown(0)= 空值 - 因为countDown(0)返回空值 (因为if条件失败),j没有值,因此yield j 不会被执行(Yield j在for循环内)。 但是,Yield i会被执行,这里i是1。所以countdown(1) 返回1给上一个递归。
b) countdown(5) for j in countDown(4)
countdown(4) for j in countDown(3)
countDown(3) for j in countDown(2)= 1。 j的值现在是'1'。所以,for j in countdown(1)成立。 因此,执行Yield j = 1。结果countDown(2)返回1, 这就是j被赋值为i的地方。
c) countdown(5) for j in countDown(4)
countdown(4) for j in countDown(3)
countDown(3) for j in countDown(2)= 1 j的值现在是'1'。所以,for j in countdown(2)成立。 因此,执行Yield j = 1。结果countDown(3)返回1
...
e) Countdown(5) for j in countDown(4) = 1 countDown(4)返回的值是1。 所以,j是1。因此,for j in countDown(4) 被执行。
The next statement Yield j within the For statement gets exceuted.
it returns 1.
for k in countDown(5),k将得到1。这将被打印出来。
2)现在进入for k in countDown(5)的第二次迭代
递归“入口”
=================
countdown(5) for j in CountDown(4) 其中j = 1(Yield j是最后执行的语句)
countdown(4) for j in CountDown(3) 其中j = 1
countDown(3) for j in CountDown(2) 其中j = 1
countdown(2) for j in CountDown(1) 其中j = 1
countdown(1) 执行Yield i,因为在上一次迭代中,步骤a) yield是最后执行的语句
递归返回
a) countdown(5) for j in CountDown(4) 其中j = 1(Yield j是最后执行的语句)
countdown(4) for j in CountDown(3) 其中j = 1
countDown(3) for j in CountDown(2) 其中j = 1
countdown(2) for j in CountDown(1) 其中j = 1 Countdown(1) 返回值为1,j已经有值1, 所以,执行下一步,即yield i,其中i = 2。 countDown(2)返回yield i = 2
b) countdown(5) for j in CountDown(4) 其中j = 1(Yield j是最后执行的语句)
countdown(4) for j in CountDown(3) 其中j = 1
countDown(3) for j in CountDown(2)
因为countDown(2)返回2,j得到这个新值j = 2,
yield j被执行。
c) countdown(5) for j in CountDown(4) 其中j = 1(Yield j是最后执行的语句)
countdown(4) for j in CountDown(3) 其中j = 1 因为countdown(3)返回的值是2,如b)所示,j的新值是2。
...
e) countdown(5) for j in CountDown(4) j变成2。
3 个回答
谢谢大家的回复。
经过进一步调试:
1) 第一次循环 - 对于 k 在 countDown(5) 中:
递归进入阶段
函数 指令
======== ===========
countdown(5) 对于 j 在 countDown(4) 中
countdown(4) 对于 j 在 countDown(3) 中
countDown(3) 对于 j 在 countDown(2) 中
countDown(2) 对于 j 在 countDown(1) 中
countDown(1) 对于 j 在 countDown(0) 中
countDown(0) 如果条件不满足,什么都不返回
递归返回阶段
a)
countdown(5) 对于 j 在 countDown(4) 中
countdown(4) 对于 j 在 countDown(3) 中
countDown(3) 对于 j 在 countDown(2) 中
countDown(2) 对于 j 在 countDown(1) 中
CountDown(1) 对于 j 在 countDown(0) 中 = 什么都没有 - 因为 countDown(0) 不返回任何值 (因为条件不满足),所以 j 也没有值,因此 yield j 不会被执行(Yield j 在 For 循环内)。 但是,Yield i 会被执行,这里的 i 是 1。所以 countdown(1) 返回 1 给上一个递归。
b)
countdown(5) 对于 j 在 countDown(4) 中
countdown(4) 对于 j 在 countDown(3) 中
countDown(3) 对于 j 在 countDown(2) 中 = 1。 现在 j 的值是 '1'。所以,countdown(1) 中的 For j 是成立的。 因此,执行 Yield j = 1。结果 countDown(2) 返回 1 这时 j 被赋值为 i。
c)
countdown(5) 对于 j 在 countDown(4) 中
countdown(4) 对于 j 在 countDown(3) 中
countDown(3) 对于 j 在 countDown(2) 中 = 1 现在 j 的值是 '1'。所以,countdown(2) 中的 For j 是成立的。 因此,执行 Yield j = 1。结果 countDown(3) 返回 1
...
e) Countdown(5) 对于 j 在 countDown(4) 中 = 1 countDown(4) 返回的值是 1。 所以,j 是 1。因此,countDown(4) 中的 for j 会被执行。
The next statement Yield j within the For statement gets exceuted.
it returns 1.
对于 k 在 countDow(5) 中,k 将得到 1。这将被打印出来。
2) 现在进入第二次循环 - 对于 k 在 countDown(5) 中
递归进入阶段
countdown(5) 对于 j 在 countDown(4) 中,j = 1(Yield j 是最后执行的语句)
countdown(4) 对于 j 在 countDown(3) 中,j = 1
countDown(3) 对于 j 在 countDown(2) 中,j = 1
countdown(2) 对于 j 在 countDown(1) 中,j = 1
countdown(1) 执行 Yield i,因为在上一次循环中,步骤 a) 的 yield 是最后执行的语句
递归返回
a)
countdown(5) 对于 j 在 countDown(4) 中,j = 1(Yield j 是最后执行的语句)
countdown(4) 对于 j 在 countDown(3) 中,j = 1
countDown(3) 对于 j 在 countDown(2) 中,j = 1
countdown(2) 对于 j 在 countDown(1) 中,j = 1 countDown(1) 返回的值是 1,j 已经有值 1, 所以,执行下一步,即 yield i,其中 i = 2。 countDown(2) 返回 yield i = 2
b)
countdown(5) 对于 j 在 countDown(4) 中,j = 1(Yield j 是最后执行的语句)
countdown(4) 对于 j 在 countDown(3) 中,j = 1
countDown(3) 对于 j 在 countDown(2) 中
因为 countDown(2) 返回 2,j 得到这个新值 j = 2
yield j 被执行
c)
countdown(5) 对于 j 在 countDown(4) 中,j = 1(Yield j 是最后执行的语句)
countdown(4) 对于 j 在 countDown(3) 中,j = 1 因为 countdown(3) 返回的值是 2,如 b) 所示,j 的新值是 2
...
e)
countdown(5) 对于 j 在 countDown(4) 中,j 变成 2
假设你在执行 countDown(5)
:
countDown(5)
会调用下一个countDown(4)
countDown(4)
会调用下一个countDown(3)
countDown(3)
会调用下一个countDown(2)
countDown(2)
会调用下一个countDown(1)
countDown(1)
会调用下一个countDown(0)
countDown(0)
会返回 0,然后countDown(0)
停止countDown(1)
返回 0countDown(2)
返回 0countDown(3)
返回 0countDown(4)
返回 0countDown(5)
返回 0countDown(5)
会调用下一个countDown(4)
countDown(4)
会调用下一个countDown(3)
countDown(3)
会调用下一个countDown(2)
countDown(2)
返回 1,然后countDown(1)
停止countDown(2)
返回 1countDown(3)
返回 1countDown(4)
返回 1countDown(5)
返回 1countDown(5)
会调用下一个countDown(4)
countDown(4)
会调用下一个countDown(3)
countDown(3)
返回 2,然后countDown(2)
停止countDown(4)
返回 2countDown(5)
返回 2countDown(5)
会调用下一个countDown(4)
countDown(4)
会调用下一个countDown(3)
countDown(3)
返回 3,然后countDown(3)
停止countDown(4)
返回 3countDown(5)
返回 3countDown(5)
会调用下一个countDown(4)
countDown(4)
返回 4,然后countDown(4)
停止countDown(5)
返回 4countDown(5)
返回 5,然后countDown(5)
停止
不知道为什么我不能编辑你的帖子。
def countDown(i):
if i <> 0:
For j in countDown( i - 1 ):
yield j
yield i
看起来你只是想倒计时,所以先返回 yield i
。这样你会得到 4 3 2 1 0
,而不是 0 1 2 3 4
def countDown(i):
yield i
if i <> 0:
For j in countDown( i - 1 ):
yield j
也许这样可以更清楚地说明调用和返回的顺序:
编辑:
def countDown(i):
if i<> 0:
for j in countDown( i - 1 ):
yield 'inner : ' + str(j)
yield 'outer : ' + str(i)
>>> for k in countDown(5): print k
...
inner : inner : inner : inner : outer : 1
inner : inner : inner : outer : 2
inner : inner : outer : 3
inner : outer : 4
outer : 5
这个过程会一直进行,直到 i 等于 0,这个时候不会返回任何值,所以在 countDown(1) 的循环中也不会返回任何东西。接下来,countDown(1) 会返回 1,这个值又会被 countDown(2)、countDown(3)、countDown(4) 和 countDown(5) 接收到。换句话说,返回的值会回到之前的调用者那里,然后不断地被返回,直到整个调用栈被解开。接下来要处理的是 countDown(2),它会把 2 返回给 countDown(3)、countDown(4) 和 countDown(5)。这些生成器会一个接一个地完成,最后 countDown(5) 会返回 5。