本地与全球
正如其他人所提到的,您将每个命名的变量都i
视为同一个变量,但事实并非如此。如果您从顶部删除f =
and行并在底部调用,您将得到相同的结果。事实上,你给这些变量取什么名字并不重要。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
.
i
ininc()
只是为传递给它的对象 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
为了修改函数内部的全局变量,您需要在执行此操作之前将其声明为全局变量。
>>> def inc(i):
... i += 1
... global n
... n += 1
...
>>> inc(i)
>>> n
11
您实际传递给函数的内容
为了让事情变得更加复杂,在将整数参数传递给 Python 时,您实际上是 按值调用。您实际上是在传递对整数对象的引用,但这些对象是静态的:
>>> id(1)
140272990224888
>>> id(2)
140272990224864
>>> i = 1
>>> id(i)
140272990224888
>>> i += 1
>>> id(i)
140272990224864
>>> id(1)
140272990224888
当我们增加i
时,它的 id 改变了。整数对象1
的 id 没有改变,id 改变了i
。这意味着这i
只是一个指向整数对象的指针的名称,当您递增时,i
您只需将其指针更改为指向整数对象,该整数对象的值比它先前指向的整数对象的值大一.
因此,即使您传递对象引用,您也只是传递整数对象0
、1
、2
、3
和的 id 4
。而且,由于您不返回这些引用或全局声明它们,因此当您的函数返回时它们失去了作用域。
等待return
您所做的另一个疏忽是您假设iterator()
返回时程序结束。当您function()
从while
循环内部调用时,iterator()
您正在等待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
一遍又一遍地看到发生的情况:您打印4
in iterator(4, [0,1,2,3])
,但不开始循环,因此开始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()