本地与全局
正如其他人所提到的,您将每个名为 i 的变量都视为同一个变量,但事实并非如此。如果您从顶部删除f = 和i = 行并在底部调用iterator(0, [0,1,2,3]),您将得到相同的结果。事实上,你给这些变量取什么名字并不重要。
看:
>>> i = 1
>>> def inc(i):
... i += 1
...
>>> inc(i)
>>> i
1
>>> k = 0
>>> inc(k)
>>> k
0
- 我为变量使用什么名称并不重要。无论您作为参数传递什么,本地都称为
i。
-
inc() 中的i 只是为传递给它的对象 ID 提供了一个本地名称。只要您的程序位于该函数的主体内,它的作用域就会持续存在。如果您不返回该变量,它会在您的函数返回时消失。
所以您可能想知道是否存在全局范围之类的东西。是的,确实如此:如果你在函数内部引用一个变量而不传递它,你会得到一个 NameError - 除非它在函数上方有作用域,像这样:
>>> n = 10
>>> def inc(i):
... i += 1
... print n
...
>>> inc(i)
10
>>> i
1
现在,这并不是说您可以对这个全局变量做任何事情。以下将不起作用:
>>> 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
要修改函数内部的全局变量,您需要先declare it as global。
>>> def inc(i):
... i += 1
... global n
... n += 1
...
>>> inc(i)
>>> n
11
你实际传递给函数的内容
为了让事情变得更加复杂,基本上 call by value 在将整数参数传递给 Python 时。您实际上是在传递对整数对象的引用,但这些对象是静态的:
>>> id(1)
140272990224888
>>> id(2)
140272990224864
>>> i = 1
>>> id(i)
140272990224888
>>> i += 1
>>> id(i)
140272990224864
>>> id(1)
140272990224888
当我们增加 i 时,它的 id 发生了变化。 1 的整数对象的 id 没有改变,i 的 id 改变了。这意味着 i 只是一个指向整数对象的指针的名称,当您增加 i 时,您只需将其指针更改为指向值比整数对象的值大 1 的整数对象它先前指向的位置。
因此,即使您传递对象引用,您也只是传递整数对象 0、1、2、3 和 4 的 ID。而且,由于您不返回这些引用,或者全局声明它们,因此当您的函数返回时它们会失去作用域。
等待return
您的另一个疏忽是您假设当iterator() 返回时,程序结束。当您从iterator() 中的while 循环内部调用function() 时,您正在等待function() 返回,然后再继续循环。 function() 然后调用iterator() 然后调用function() 然后调用......你明白了。当您完成带有 4, 3, iterate. 废话返回的最终循环时,您会看到自己再次进入 3, 3, iterate. 循环 - 但 那个 循环永远不会返回。
当你从一个函数内部调用另一个函数时,你必须等待另一个函数返回才能继续下一条语句。
>>> def foo():
... bar()
... print "world!"
...
>>> def bar():
... print "hello"
...
>>> foo()
hello
world!
foo() 无法打印 "world!",直到 bar() 返回。如果你改了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
哎呀。两个函数都无法返回,因为它正在等待对另一个的调用,并且每次一个函数调用另一个时都会创建一个新堆栈 - 最终堆栈溢出。
如果您不了解堆栈的工作原理,那么这不在我(非常冗长)答案的范围内。你需要查一下。
混合控制结构
通过两个相互调用的函数,您创建了一个递归控制结构。此外,通过使用 while 循环而不更改循环条件,您创建了一个无限循环。因此,虽然您的递归有一个基本情况(i >= len(f) 因为i < len(f) 是递归情况),但您的无限循环将导致程序一遍又一遍地调用该基本情况。
究竟发生了什么(调用堆栈)
-
iterator(0, [0,1,2,3]) 呼叫并等待
-
function(0, [0,1,2,3]) 呼叫并等待
-
iterator(1, [0,1,2,3]) 呼叫等待
-
function(1, [0,1,2,3]) 呼叫等待
-
iterator(2, [0,1,2,3]) 呼叫等待
-
function(2, [0,1,2,3]) 呼叫并等待
-
iterator(3, [0,1,2,3]) 呼叫等待
-
function(3, [0,1,2,3]) 呼叫等待
-
iterator(4, [0,1,2,3])返回
-
function(3, [0,1,2,3])返回
-
iterator(3, [0,1,2,3]) 循环,然后调用并等待
-
function(3, [0,1,2,3]) 等
这就是为什么你会看到4, 3, iterate 一遍又一遍地发生:你在iterator(4, [0,1,2,3]) 中打印4,但不开始循环,所以iterator(3, [0,1,2,3]) 开始循环,然后你打印3, iterate. 然后你回到iterator(4, [0,1,2,3]),再次打印4,以此类推。因为iterator(4, [0,1,2,3]) 返回,所以不会出现堆栈溢出,但仍然会出现无限循环。
如何解决
如果你想要递归:
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)
如果你想要迭代:
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)
或者,如果您将这些变量声明为全局变量,那么更改会持续存在:
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()