我有下面的代码,由于某种原因,while循环没有终止。
def iterator(i, f):
print len(f)
print i
while i < len(f):
if i == 0:
print "Restarted."
else:
print "iterate."
function()
return f
打印语句不是必需的,但计数器i
会被另一个函数增加,所以我想确保它不是问题。对于包含4个项目的列表f
,它打印如下:
4, 0, Restarted.
4, 1, iterate.
4, 2, iterate.
4, 3, iterate.
4, 4, iterate.
4, 4, iterate.
etc..
我不明白为什么它会在i = 4
和len(f) = 4
时继续进入while循环。它应该打破循环并执行返回函数,但由于某种原因它不会。
任何人都可以解释为什么while循环在条件变为false时不会终止?
编辑:一些代码可以更好地解释发生了什么。我还澄清了i
function()
的更改,iterator
随后i
调用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."
function(i, f)
return f
def function(i, f):
i += 1
iterator(i, f)
iterator(i,f)
。
希望这是有道理的。
0, 0, Restarted.
1, 1, iterate.
2, 2, iterate.
3, 3, iterate.
4, 3, iterate.
4, 3, iterate.
etc.
结果如下:
{{1}}
答案 0 :(得分:6)
计数器i被另一个函数增加
不可能。它是一个局部整数。打印的值是不同变量的值。尝试在循环中添加print i
,然后您就会看到。
int
是不可变类型。这意味着此类型的对象无法更改其值。唯一可以改变的是某个变量所持有的对象。
def inc(i):
i+=1
i=0
inc(i)
print i
输出:
0
这是为什么?因为i
内的inc
和i
外的i+=1
是两个独立变量。 inc
内的i
仅表示&#34;让局部变量2
现在指向新对象i
&#34;。它不会以任何方式影响全局function
。就像C和Java(以及大多数其他主要语言中的默认语言)一样,变量按值传递 。
此循环可以结束的唯一方法是f
从f
删除元素,假设f = [0,1,2,3]
i = 0
def iterator(i, f):
print i
while i < len(f):
'''if the recursion is less than 4 levels deep: loop forever
else: don't loop at all'''
print i
if i == 0:
print "Restarted."
else:
print "iterate."
iterator(i+1, f)
return f
是一个列表,因此是可变的。
这是新版本的等效代码。请注意,您正在使用递归,而不仅仅是循环。你很快就会失业:
0, 0, Restarted. # 0 level recursion, 1st iteration
1, 1, iterate. # 1 level recursion, 1st iteration
2, 2, iterate. # 2 level recursion, 1st iteration
3, 3, iterate. # 3 level recursion, 1st iteration
4, 3, iterate. # 3 level recursion, 2nd iteration, the condition now is false
4, 3, iterate. # 3 level recursion, 3rd iteration
4, 3, iterate. # 3 level recursion, 4th iteration
4, 3, iterate. # 3 level recursion, 5th iteration
输出:
{{1}}
等等。
答案 1 :(得分:2)
您的问题是您没有正确理解全局变量和局部变量。名称i
表示循环外部的内容与循环内部不同。试试看我的意思:
def iterator(i, f):
# Your code
def function(i, f):
# Your code
iterator(0, [0, 1, 2, 3])
当然,即使全局没有定义i
,这仍然会运行。那是因为你有一个名为i
的函数参数,使i
在你的函数中具有新的含义。因为数字是不可变的,所以当你在函数内“改变”i
时,你只是说“好吧,现在i
将指向此函数内的不同数字”。你没有改变基础数字。
有两种方法可以解决您的问题。快速而肮脏的方法是从两个函数中删除参数。这将使i
仅引用全局变量i
:
i = 0
f = range(4)
def iterator():
# etc
def function():
# etc
iterator()
不鼓励使用像这样的全局变量,因为它不干净,很难调试,其他功能可能会得到它并导致意外行为......通常不是一个好主意。相反,您可以保留函数签名,但可以使用返回值来执行您想要执行的任何操作。像这样:
global_f = [0,1,2,3]
global_i = 0
def iterator(i, f):
while i < len(f):
if i == 0:
print "Restarted."
else:
print "iterate."
i = function(i, f) # Reassign i or it won't be changed!!
return f
def function(i, f):
return i + 1
iterator(global_i, global_f)
请注意,我不确定假设的<{1}} 要做什么,所以我将其简化为简单的return语句。当两个函数一般相互调用时要小心;在进行递归时很容易进入无限循环!
答案 2 :(得分:1)
正如其他人所提到的,你将每个名为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
。i
中的inc()
只为传递给它的对象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
为了让事情更复杂,在将整数参数传递给Python时,本质上是 call by value。您实际上是将引用传递给整数对象,但这些对象是静态的:
>>> 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
和{的id。 {1}}。并且,由于您没有返回这些引用,或者在全局声明它们,因此当函数返回时它们会失去作用域。
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()
Woops。这两个函数都没有返回,因为它正在等待对另一个函数的调用,每次一个函数调用另一个函数创建一个新的堆栈时 - 最终堆栈溢出。
如果您不理解堆栈是如何工作的,那么这不在我(令人难以置信的冗长)答案的范围内。你需要看一下。
通过两个相互调用的函数,您创建了一个递归控制结构。此外,通过使用while循环而不更改循环条件,您创建了一个无限循环。因此,虽然递归具有基本情况(>>> 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
,因为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
,但是没有开始循环,所以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)