【问题标题】:Scope of python variable in for loopfor循环中python变量的范围
【发布时间】:2013-03-12 13:53:29
【问题描述】:

这是我遇到问题的 Python 代码:

for i in range (0,10):
    if i==5:
        i+=3
    print i

我预计输出是:

0
1
2
3
4
8
9

但是,解释器吐了出来:

0
1
2
3
4
8
6
7
8
9

我知道for 循环为 C 中的变量创建了一个新范围,但不知道 Python。为什么i的值在Python的for循环中没有变化,有什么补救措施可以得到预期的输出?

【问题讨论】:

  • 它正在按照你说的去做 - 基本上,打印每个 i - 但如果 i 最初是 5 - 将其改为 8...你试图以某种方式将i 视为指向范围的指针——Python 不能那样工作
  • 标识在Python中是什么意思,请更正你的问题。
  • 你怎么把它改成8??
  • 抱歉缩进
  • 我注意到您使用制表符而不是空格进行缩进(我已修复它以在此处显示预期的缩进)。真的不推荐,我会用空格代替。

标签: python for-loop scope


【解决方案1】:

for循环遍历range(10)中的所有数字,即[0,1,2,3,4,5,6,7,8,9]
您更改i当前 值不会影响范围内的下一个值。

您可以通过 while 循环获得所需的行为。

i = 0
while i < 10:
    # do stuff and manipulate `i` as much as you like       
    if i==5:
        i+=3

    print i

    # don't forget to increment `i` manually
    i += 1

【讨论】:

    【解决方案2】:

    类比 C 代码

    你想象你在 python 中的for-loop 是这样的 C 代码:

    for (int i = 0; i < 10; i++)
        if (i == 5)
            i += 3;
    

    更像是这样的 C 代码:

    int r[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    for (int j = 0; j < sizeof(r)/sizeof(r[0]); j++) {
        int i = r[j];
        if (i == 5)
            i += 3;
    }
    

    所以在循环中修改i并没有你期望的效果。

    反汇编示例

    您可以查看disassembly of the python code 看到这个:

    >>> from dis import dis
    >>> def foo():
    ...     for i in range (0,10):
    ...         if i==5:
    ...             i+=3
    ...         print i
    ... 
    >>> dis(foo)
      2           0 SETUP_LOOP              53 (to 56)
                  3 LOAD_GLOBAL              0 (range)
                  6 LOAD_CONST               1 (0)
                  9 LOAD_CONST               2 (10)
                 12 CALL_FUNCTION            2
                 15 GET_ITER            
            >>   16 FOR_ITER                36 (to 55)
                 19 STORE_FAST               0 (i)
    
      3          22 LOAD_FAST                0 (i)
                 25 LOAD_CONST               3 (5)
                 28 COMPARE_OP               2 (==)
                 31 POP_JUMP_IF_FALSE       47
    
      4          34 LOAD_FAST                0 (i)
                 37 LOAD_CONST               4 (3)
                 40 INPLACE_ADD         
                 41 STORE_FAST               0 (i)
                 44 JUMP_FORWARD             0 (to 47)
    
      5     >>   47 LOAD_FAST                0 (i)
                 50 PRINT_ITEM          
                 51 PRINT_NEWLINE       
                 52 JUMP_ABSOLUTE           16
            >>   55 POP_BLOCK           
            >>   56 LOAD_CONST               0 (None)
                 59 RETURN_VALUE        
    >>> 
    

    这部分creates a range between 0 and 10并实现:

              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (0)
              9 LOAD_CONST               2 (10)
             12 CALL_FUNCTION            2
    

    此时,栈顶包含范围。

    这个gets an iterator over the object on the top of the stack,即范围:

             15 GET_ITER  
    

    此时,堆栈的顶部包含一个已实现范围的迭代器。

    FOR_ITER begins iterating over the loop 使用堆栈顶部的迭代器:

        >>   16 FOR_ITER                36 (to 55)
    

    此时,栈顶包含迭代器的下一个值。

    在这里你可以看到the top of the stack is popped and assigned to i

             19 STORE_FAST               0 (i)
    

    所以无论你在循环中做什么,i 都会被覆盖。

    这里是overview of stack machines,如果您以前没有见过的话。

    【讨论】:

      【解决方案3】:

      Python 中的 for 循环实际上是一个 for-each 循环。在每个循环开始时,i 设置为迭代器中的下一个元素(在您的情况下为range(0, 10))。 i 的值会在每个循环开始时重新设置,因此在循环体中更改它不会更改其在下一次迭代中的值。

      即你写的for循环等价于下面的while循环:

      _numbers = range(0, 10) #the list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
      _iter = iter(_numbers)
      while True:
          try:
              i = _iter.next()
          except StopIteration:
              break
      
          #--YOUR CODE HERE:--
          if i==5:
              i+=3
          print i
      

      【讨论】:

        【解决方案4】:

        如果由于某种原因您确实想在等于5 时将 add 3 更改为 i,并跳过下一个元素(这是在 C 3 元素中推进指针的一种方式),那么您可以使用一个迭代器并从中消耗一些位:

        from collections import deque
        from itertools import islice
        
        x = iter(range(10)) # create iterator over list, so we can skip unnecessary bits
        for i in x:
            if i == 5:             
                deque(islice(x, 3), 0) # "swallow up" next 3 items
                i += 3 # modify current i to be 8
            print i
        
        0
        1
        2
        3
        4
        8
        9
        

        【讨论】:

        • 我认为 Junuxx 答案中的 while 循环更加整洁。但是,如果您无法通过简单的操作(如 +=3)在序列中跳转,则此答案很有用
        【解决方案5】:

        在 python 2.7 中,range 函数创建一个列表,而在 python 3.x 版本中,它创建一个“范围”类对象,该对象只能迭代而不是列表,类似于 python 2.7 中的 xrange。

        不是当你迭代 range(1, 10) 时,最终你是从列表类型对象中读取,并且每次到达 for 循环时 i 都会获取新值。

        这有点像:

        for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
            if i==5:
                i+=3
            print(i)
        

        更改值不会更改列表中的迭代顺序。

        【讨论】:

          【解决方案6】:

          我每次迭代都会重置,所以你在循环中对它做了什么并不重要。它做任何事情的唯一时间是当 i 为 5 时,然后它加 3。一旦它循环回来,它就会将 i 设置回列表中的下一个数字。您可能想在这里使用while

          【讨论】:

            【解决方案7】:

            Python 的for 循环简单地循环提供的值序列——将其视为“foreach”。因此,修改变量对循环执行没有影响。

            这在the tutorial中有很好的描述。

            【讨论】:

              【解决方案8】:
              it = iter(xrange (0,10))
              for i in it:
                  if i==4: all(it.next() for a in xrange(3))
                  print i
              

              it = iter(xrange (0,10))
              itn = it.next
              for i in it:
                  if i==4: all(itn() for a in xrange(3))
                  print i
              

              【讨论】:

                【解决方案9】:

                您可以对 for 循环进行以下修改:

                for i in range (0,10):
                    if i in [5, 6, 7]:
                        continue
                    print(i)
                

                【讨论】:

                  【解决方案10】:

                  在我看来,类似的代码不是 while 循环,而是 for 循环,您可以在运行时编辑列表:

                  originalLoopRange = 5
                  loopList = list(range(originalLoopRange))
                  timesThroughLoop = 0
                  for loopIndex in loopList:
                      print(timesThroughLoop, "count")
                      if loopIndex == 2:
                          loopList.pop(3)
                          print(loopList)
                      print(loopIndex)
                      timesThroughLoop += 1
                  

                  【讨论】:

                  • 如果计数器变量没有更新,while 循环是无限的。在其他语言中,除非有人弄乱了计数器,否则 for 循环不是无限的。在 python 中,创建一个通常不是无限的循环,并且你可以将“计数器”弄得更短或更长,我发布的代码可以更正确地表示(在我看来)。您可以从列表中插入或弹出,这会增加或减少循环的长度。
                  • 如果你什么都不做,循环不是无限的。因此(在我看来)这更像是原始海报所要求的。也许更重要的是,这就是其他类似问题所问的问题。虽然从某种意义上说它是“危险的”,但通过插入循环列表,可以再次使其无限。
                  猜你喜欢
                  • 1970-01-01
                  • 2017-11-30
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-05-27
                  相关资源
                  最近更新 更多