Let me try:
From your snippet, in every function call i.e foo(step+1) a structure known as an activation record or frame is created to store information about the progress of that invocation of the function. So, When the execution of a function leads to a nested function call, the execution of the former call is suspended and its activation record stores the place in the source code at which the flow of control should continue upon return of the nested call.
Here is the main part:
When step == 4, which in turns range(4,4) == empty list, that time iteration won't happen so it will return None. Then it will move to the previous frame, where it was stopped and start a new iteration and recursive function call until range(4,4).
NB: Recursive base case is only when step == 4, that time range(4,4) and return None.
Every recusion needs a base case other wise it will goto infinite loop.
So, lets see the recursive trace: I am adding i to differentiate the step and iterative increment.
# 1 def foo(step=0): # 2 for i in range(step, 4): # 3 print 'i: %d, step: %d' % (i,step) # 4 foo(step+1) # 5 foo() line 5 line 1 foo with step=0 which is default line 2 range(0,4) Frame: A, 0 is over, next=1 line 3 step = 0 Output: i: 0, step: 0 line 4 calling foo(0 + 1) line 1 foo with step=1 line 2 range(1,4) Frame: B, 1 is over, next=2 line 3 step = 1 Output: i: 1, step: 1 line 4 calling foo(1 + 1) line 1 foo with step=2 line 2 range(2,4) Frame: C, 2 is over, next=3 line 3 step = 2 Output: i: 2, step: 2 line 4 calling foo(2 + 1) line 1 foo with step=3 line 2 range(3,4) Frame: D, 3 is over, next=4 line 3 step = 3, Output: i: 3, step: 3 line 4 calling foo(3 + 1) line 1 foo with step=4 line 2 range(4,4) Frame: E, This is an empty list, so it won't come inside the loop, so return None. Come back to previous Frame: D, i=3 was used, now increment to 4. So, again range(4,4) line 2 range(4,4) Empty list, from Frame: D, return None Come back to previous Frame C, now i=3, step was called with value 2 line 2 range(2,4) line 3 step = 2 Output: i: 3, step: 2, why step == 2 because the function foo was called with step=2 line 4 calling foo(2 + 1) line 1 foo with step=3 line 2 range(3,4) line 3 step = 3, Output : i: 3, step: 3 line 4 calling foo(3 + 1) line 1 foo with step=4 line 2 range(4,4) Empty list again, not going inside the list, return None line 2 range(2,4) From Frame: B, step was == 1, because the function foo was called with step=1 line 3 step: 1 Output: i: 2, step: 1, here i ==2, because this is the second iteration of Frame B. line 4 calling foo(1 + 1) line 1 foo with step=2 line 2 range(2,4) line 3 step: 2 Output: i: 2, step: 2
After this it follows the same recursive fashion, until the iterative range is exhuausted i.e range(4,4)
Please let me know if that helps.
print range(step, 4)before the for loop.