在Python中循环终止

时间:2013-07-01 13:38:55

标签: python python-2.7 while-loop

我有下面的代码,由于某种原因,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 = 4len(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}}

3 个答案:

答案 0 :(得分:6)

  

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

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

int不可变类型。这意味着此类型的对象无法更改其值。唯一可以改变的是某个变量所持有的对象。

def inc(i):
    i+=1

i=0
inc(i)
print i

输出:

0
这是为什么?因为i内的inci外的i+=1是两个独立变量。 inc内的i仅表示&#34;让局部变量2现在指向新对象i&#34;。它不会以任何方式影响全局function。就像C和Java(以及大多数其他主要语言中的默认语言)一样,变量按值传递

此循环可以结束的唯一方法是ff删除元素,假设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
  1. 我用于变量的名称无关紧要。无论你作为参数传递的是 local ,都称为i
  2. i中的inc()只为传递给它的对象ID提供了一个本地名称。只要您的程序在该函数体内,它的范围就会持续。如果不返回该变量,则在函数返回时它会消失。
  3. 所以你可能想知道是否有全球范围这样的事情。是的,的确如此:如果你在一个函数内部引用一个变量而没有传递它,那么你将得到一个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它以前指向的整数对象。

    因此,即使您正在传递对象引用,也只是传递整数对象0123和{的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)