Local vs global
As others have mentioned, you're treating every variable named i as if it is the same variable, but it's not. If you remove the f = and i = lines from the top and call iterator(0, [0,1,2,3]) at the bottom instead, you'll get the same thing. In fact, it doesn't matter at all what you name these variables.
Look:
>>> i = 1
>>> def inc(i):
... i += 1
...
>>> inc(i)
>>> i
1
>>> k = 0
>>> inc(k)
>>> k
0
- It didn't matter what name I used for a variable. Whatever you pass as an argument is locally called
i.
- The
i in inc() just gives a local name to the object id passed into it. Its scope lasts so long as your program is inside the body of that function. If you don't return that variable, it disappears when your function returns.
So you may wonder if there even is such a thing as global scope. Yes, indeed: if you refer to a variable inside of a function without passing it, you'll get a NameError - unless it has scope above the function, like so:
>>> n = 10
>>> def inc(i):
... i += 1
... print n
...
>>> inc(i)
10
>>> i
1
Now, that's not to say you can just do anything with this global variable. The following won't work:
>>> def inc(i):
... i += 1
... n += 1
...
>>> inc(i)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in inc
UnboundLocalError: local variable 'n' referenced before assignment
In order to modify a global variable inside of a function, you need to declare it as global before you do that.
>>> def inc(i):
... i += 1
... global n
... n += 1
...
>>> inc(i)
>>> n
11
What you're actually passing to the functions
To make things more convoluted, you essentially call by value when passing integer arguments to Python. You are actually passing references to integer objects, but these objects are static:
>>> id(1)
140272990224888
>>> id(2)
140272990224864
>>> i = 1
>>> id(i)
140272990224888
>>> i += 1
>>> id(i)
140272990224864
>>> id(1)
140272990224888
When we incremented i, its id changed. The id of the integer object for 1 didn't change, the id for i did. This means that i is just a name for a pointer to an integer object, and when you increment i you just change its pointer to point to the integer object which has a value of one greater than the value of the integer object to which it was previously pointing.
So, even though you are passing object references, you're just passing the ids for the integer objects 0, 1, 2, 3, and 4. And, since you don't return those references, or declare them globally, they lose scope when your function returns.
Waiting for return
Another oversight you've made is that you assume that when iterator() returns, the program ends. When you call function() from inside the while loop in iterator() you are waiting for function() to return before you continue the loop. function() then calls iterator() which then calls function() which then calls... you get the idea. When you have your final loop with the 4, 3, iterate. nonsense return, you see yourself enter the 3, 3, iterate. loop again - but that loop never returns.
When you call another function from inside a function, you have to wait for that other function to return before you can continue to the next statement.
>>> def foo():
... bar()
... print "world!"
...
>>> def bar():
... print "hello"
...
>>> foo()
hello
world!
foo() couldn't print "world!" until bar() returned. If you changed bar():
>>> def bar():
... print "hello"
... foo()
...
>>> foo()
hello
hello
hello
hello
hello
hello
...
File "<stdin>", line 3, in bar
File "<stdin>", line 2, in foo
File "<stdin>", line 3, in bar
File "<stdin>", line 2, in foo
RuntimeError: maximum recursion depth exceeded
>>>
KeyboardInterrupt
Woops. Neither function could return because it was waiting on a call to the other, and each time a function called the other a new stack from was created - and eventually the stack overflowed.
If you don't understand how the stack works, well, that's not in the scope of my (incredibly verbose) answer. You need to look that up.
Mixing control structures
By having two functions which call each other, you've created a recursive control structure. Additionally, by using a while loop without ever changing the loop condition, you've created an infinite loop. So while your recursion has a base case (i >= len(f) because i < len(f) is the recursive case), your infinite loop will cause the program to call that base case over and over.
What exactly is happening (the call stack)
iterator(0, [0,1,2,3]) calls and waits for
function(0, [0,1,2,3]) calls and waits for
iterator(1, [0,1,2,3]) calls and waits for
function(1, [0,1,2,3]) calls and waits for
iterator(2, [0,1,2,3]) calls and waits for
function(2, [0,1,2,3]) calls and waits for
iterator(3, [0,1,2,3]) calls and waits for
function(3, [0,1,2,3]) calls and waits for
iterator(4, [0,1,2,3]) returns
function(3, [0,1,2,3]) returns
iterator(3, [0,1,2,3]) loops, then calls and waits for
function(3, [0,1,2,3]) etc.
This is why you see 4, 3, iterate happening over and over: you print 4 in iterator(4, [0,1,2,3]), but don't begin the loop, so iterator(3, [0,1,2,3]) gets to loop and you print 3, iterate. and then you go back into iterator(4, [0,1,2,3]) which prints 4 again, and so on. Because iterator(4, [0,1,2,3]) returns, you don't get a stack overflow, but you do still get an infinite loop.
How to fix it
If you want recursion:
f = [0,1,2,3]
i = 0
def iterator(i, f):
print i
if i < len(f):
print i
if i == 0:
print "Restarted."
else:
print "iterate."
function(i, f)
return f
def function(i, f):
i += 1
iterator(i, f)
iterator(i,f)
If you want iteration:
f = [0,1,2,3]
i = 0
def iterator(i, f):
print i
while i < len(f):
print i
if i == 0:
print "Restarted."
else:
print "iterate."
i += 1
return f
iterator(i,f)
Or, if you declare these variables as global, so that changes persist:
f = [0,1,2,3]
i = 0
def iterator():
global i
global f
print i
while i < len(f):
print i
if i == 0:
print "Restarted."
else:
print "iterate."
function()
return f
def function():
global i
i += 1
iterator()
iterator()
function()calliterator()?function, or at least the relevant parts so that we can run the code and reproduce your results.