1

我有下面的代码,由于某种原因,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被另一个函数增加了,所以我想确保它不是问题。对于f包含 4 个项目的列表,它会打印如下:

4, 0, Restarted.
4, 1, iterate.
4, 2, iterate.
4, 3, iterate.
4, 4, iterate.
4, 4, iterate.
etc..

i = 4我不明白为什么它会一直进入while循环len(f) = 4. 它应该打破循环并执行返回函数,但由于某种原因它没有。

谁能解释为什么当条件变为假时while循环不会终止?

编辑:一些代码可以更好地解释发生了什么。我还澄清了i变化 by 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.
4

3 回答 3

6

计数器 i 被另一个函数增加

不可能的。它是一个局部整数。打印的值是不同变量的值。尝试print i在循环内添加,你会看到。

int不可变类型。这意味着这种类型的对象不能改变它的值。唯一可以改变的是某个变量所持有的对象。

def inc(i):
    i+=1

i=0
inc(i)
print i

输出:

0

为什么是这样?因为i里面inci外面是两个自变量。i+=1里面只表示“inc让局部变量i现在指向一个新对象2”。它不会i以任何方式影响全局。就像 C 和 Java(以及大多数其他主要语言的默认设置)一样,变量是按值传递的。

这个循环可以结束的唯一方法是如果function从中删除元素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 

等等。

于 2013-07-01T13:40:09.953 回答
2

您的问题是您没有正确理解全局变量和局部变量。该名称i在循环外的含义与在循环内的含义不同。试试这个看看我的意思:

def iterator(i, f):
    # Your code

def function(i, f):
    # Your code

iterator(0, [0, 1, 2, 3])

当然,这仍然会运行,即使没有i全局定义。那是因为你有名为 的函数参数ii在你的函数中具有新的含义。因为数字是不可变的,所以每当您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)

请注意,我不确定该做什么,所以我将其简化为简单的 return 语句function。当两个函数通常相互调用时要小心;递归时很容易陷入无限循环!

于 2013-07-01T14:06:27.617 回答
1

本地与全球

正如其他人所提到的,您将每个命名的变量都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
  1. 我为变量使用什么名称并不重要。无论您作为参数传递什么,都会在本地调用i.
  2. iininc()只是为传递给它的对象 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您只需将其指针更改为指向整数对象,该整数对象的值比它先前指向的整数对象的值大一.

因此,即使您传递对象引用,您也只是传递整数对象0123和的 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一遍又一遍地看到发生的情况:您打印4in 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()
于 2013-07-01T17:44:56.273 回答