简体   繁体   中英

Python Generator

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

On debugging this code , the execution path was bit different from what I expected. My expectation: The "for" loop in countDown will call the function recursively: countDown(4), countDown(3), countDown(2) , and countDown(1) and countDown(0). At this point the if condition is not satisfied then the "return phase" starts, Yield j will be executed . This will return the control back to for loop in "main" function.

The next iteration of this for loop , again calls countDown(5) which starts where the last Yield left off - Yield j. This executes the for ... countdown(i-1) loop, and calls countDown(4), countDown(3) , CountDown(2).. countDown(0). Then the "return phase" begins and hit the first Yield j( j is now incremented from the first pass earlier). This will return the control back to the for loop in the "main" function.

On debug, this is what I found:

1)once it fails if i<>0 as explained in at the end of recursion, it seems to execute Yield i. Then it seems to Pop out every countDown in Stack before the control goes back to for k ... main.

2) in the Second iteration For k in g , stack enters return phase - top to botom of the stack is countDown(1) ,countDown(2),countDown(3), countDown(4) then it starts "return phase". In this phase j has the value of '1'. So. how come it does not go down to countDown(0) where i = 1 before starting the return phase?

Can anyone please explains the how this is supposed to work - combination of recursion and Generator function? Sorry for the long windy post- had no other way to explain it

Thx. guys for the reply.

My understanding so far :

1) first iteration of for k in countDown(5): entry

Function Instruction

======== ===========
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 statement fails and returns nothing

return phase

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)= nothing - since countDown(0) returns nothing (because of failed if condition), j has nothing annd hence yield j, does not get excuted(Yield j is within the For loop). But, Yield i gets executed ,i here is 1 . So countdown(1) returns 1 to the previous recursion.

b) 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)= 1. The value of j is now '1'. SO, the For j in countdown(1) is true . Hence, it excutes Yield j = 1. As a result countDown(2) returns 1 This is where j gets assigned i.

c) countdown(5) for j in countDown(4)

countdown(4) for j in countDown(3)

countDown(3) for j in countDown (2)= 1 The value of j is now '1'. SO, the For j in countdown(2) is true . Hence, it excutes Yield j = 1. As a result countDown(3) returns 1

...

e) Countdown(5) for j in countDown(4) = 1 The value returned by count Down(4) is 1 . Hence, j is 1. So, the for j in countDown(4) gets executed.

               The next statement Yield j  within the For statement gets exceuted.
                it returns 1.

for k in countDow(5) , k will get 1.This will be printed out.

2)Now to the second iteration of for k in countDown(5)

recursion "Entry"

=================

countdown(5) for j in countDown(4) where j = 1 (Yield j was the last statement executed)

countdown(4) for j in countDown(3) where j = 1

countDown(3) for j in countDown (2) where j = 1

countdown(2) for j in countDown(1) where j = 1

countdown(1) executes Yield i , since the previous iteration, step a) yield was the last statement exceuted

recursion return

a) countdown(5) for j in countDown(4) where j = 1 (Yield j was the last statement executed)

countdown(4) for j in countDown(3) where j = 1

countDown(3) for j in countDown (2) where j = 1

countdown(2) for j in countDown(1) where j = 1 Countdown(1) returns a value of 1, j already has a value of 1, so, it executes next step which is yield i where i = 2. countDown(2) returns yield i = 2

b) countdown(5) for j in countDown(4) where j = 1 (Yield j was the last statement executed)

countdown(4) for j in countDown(3) where j = 1

countDown(3) for j in countDown (2)
since countDown(2) returns 2, j gets this new value j = 2 yield j gets executed c)

countdown(5) for j in countDown(4) where j = 1 (Yield j was the last statement executed)

countdown(4) for j in countDown(3) where j = 1 since countdown(3) returns a value of 2 as shown in b) the new value of j is 2

...

e) countdown(5) for j in countDown(4) j becomes 2

Maybe this will clarify the order in which the calls/yields occur:

Edit:

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

It pushes in all the way until i is 0, which doesn't yield anything, so neither does the for loop in countDown(1). countDown(1) next yields 1, which returns back to be yielded by countDown(2), countDown(3), countDown(4), and countDown(5). In other words, the yielded value returns to the previous caller, where it is yielded again and again until the call stack is unwound. The next generator to be exhausted is countDown(2), which yields 2 back to countDown(3), countDown(4), and countDown(5). The generators exhaust in succession until at last countDown(5) yields 5.

Let's say you iterate over countDown(5) :

  1. countDown(5) fetch next countDown(4)
  2. countDown(4) fetch next countDown(3)
  3. countDown(3) fetch next countDown(2)
  4. countDown(2) fetch next countDown(1)
  5. countDown(1) fetch next countDown(0)
  6. countDown(0) yield i = 0, countDown(0) stop
  7. countDown(1) yield j = 0
  8. countDown(2) yield j = 0
  9. countDown(3) yield j = 0
  10. countDown(4) yield j = 0
  11. countDown(5) yield j = 0
  12. countDown(5) fetch next countDown(4)
  13. countDown(4) fetch next countDown(3)
  14. countDown(3) fetch next countDown(2)
  15. countDown(2) fetch next countDown(1)
  16. countDown(1) yield i = 1, countDown(1) stop
  17. countDown(2) yield j = 1
  18. countDown(3) yield j = 1
  19. countDown(4) yield j = 1
  20. countDown(5) yield j = 1
  21. countDown(5) fetch next countDown(4)
  22. countDown(4) fetch next countDown(3)
  23. countDown(3) fetch next countDown(2)
  24. countDown(2) yield i = 2, countDown(2) stop
  25. countDown(3) yield j = 2
  26. countDown(4) yield j = 2
  27. countDown(5) yield j = 2
  28. countDown(5) fetch next countDown(4)
  29. countDown(4) fetch next countDown(3)
  30. countDown(3) yield i = 3, countDown(3) stop
  31. countDown(4) yield j = 3
  32. countDown(5) yield j = 3
  33. countDown(5) fetch next countDown(4)
  34. countDown(4) yield i = 4, countDown(4) stop
  35. countDown(5) yield j = 4
  36. countDown(5) yield i = 5, countDown(5) stop

Don't know why I couldn't edit your post.

def countDown(i):
    if i <> 0:
        For j in countDown( i - 1 ):
            yield j
    yield i

It seems you just want count down, so yield i first. Then you will get 4 3 2 1 0 instead of 0 1 2 3 4

def countDown(i):
    yield i
    if i <> 0:
        For j in countDown( i - 1 ):
            yield j

Thx. guys for the reply.

After further debug :

1) first iteration - for k in countDown(5):

recursion entry phase

Function Instruction

======== ===========
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 statement fails and returns nothing

recursion return phase

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)= nothing - since countDown(0) returns nothing (because of failed if condition), j has nothing annd hence yield j, does not get excuted(Yield j is within the For loop). But, Yield i gets executed ,i here is 1 . So countdown(1) returns 1 to the previous recursion.

b) 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)= 1. The value of j is now '1'. SO, the For j in countdown(1) is true . Hence, it excutes Yield j = 1. As a result countDown(2) returns 1 This is where j gets assigned i.

c) countdown(5) for j in countDown(4)

countdown(4) for j in countDown(3)

countDown(3) for j in countDown (2)= 1 The value of j is now '1'. SO, the For j in countdown(2) is true . Hence, it excutes Yield j = 1. As a result countDown(3) returns 1

...

e) Countdown(5) for j in countDown(4) = 1 The value returned by count Down(4) is 1 . Hence, j is 1. So, the for j in countDown(4) gets executed.

               The next statement Yield j  within the For statement gets exceuted.
                it returns 1.

for k in countDow(5) , k will get 1.This will be printed out.

2)Now to the second iteration - for k in countDown(5)

recursion entry phase

countdown(5) for j in countDown(4) where j = 1 (Yield j was the last statement executed)

countdown(4) for j in countDown(3) where j = 1

countDown(3) for j in countDown (2) where j = 1

countdown(2) for j in countDown(1) where j = 1

countdown(1) executes Yield i , since the previous iteration, step a) yield was the last statement exceuted

recursion return

a) countdown(5) for j in countDown(4) where j = 1 (Yield j was the last statement executed)

countdown(4) for j in countDown(3) where j = 1

countDown(3) for j in countDown (2) where j = 1

countdown(2) for j in countDown(1) where j = 1 Countdown(1) returns a value of 1, j already has a value of 1, so, it executes next step which is yield i where i = 2. countDown(2) returns yield i = 2

b) countdown(5) for j in countDown(4) where j = 1 (Yield j was the last statement executed)

countdown(4) for j in countDown(3) where j = 1

countDown(3) for j in countDown (2)
since countDown(2) returns 2, j gets this new value j = 2 yield j gets executed c)

countdown(5) for j in countDown(4) where j = 1 (Yield j was the last statement executed)

countdown(4) for j in countDown(3) where j = 1 since countdown(3) returns a value of 2 as shown in b) the new value of j is 2

...

e) countdown(5) for j in countDown(4) j becomes 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM