繁体   English   中英

在循环期间更改可迭代变量

[英]Changing iterable variable during loop

让它it python中的可迭代元素。 在什么情况下, it在一个循环内的变化反映了it 或者更直接:这样的事情什么时候起作用?

it = range(6)
for i in it:
    it.remove(i+1)
    print i

导致打印0,2,4(显示循环运行3次)。

另一方面呢

it = range(6)
for i in it:
    it = it[:-2]
    print it

导致输出:

[0,1,2,3]
[0,1]
[]
[]
[]
[],

显示循环运行6次。 我想这与就地操作或可变范围有关,但不能100%肯定地绕过它。

Clearification:

一个例子,不起作用:

it = range(6)
for i in it:
    it = it.remove(i+1)
    print it

导致'None'被打印并且Error(NoneType没有属性'remove')被抛出。

当您遍历list ,实际上调用list.__iter__() ,它返回绑定到listlistiterator对象,然后实际迭代此listiterator 从技术上讲,这个:

itt = [1, 2, 3]
for i in itt:
    print i

实际上是一种语法糖:

itt = [1, 2, 3]
iterator = iter(itt)
while True:
    try:
        i  = it.next()
    except StopIteration:
        break
    print i

所以在这一点 - 在循环中 - 重新绑定itt不会影响listiterator (它保持自己对列表的引用),但是变异 itt显然会影响它(因为两个引用都指向同一个列表)。

如果重新绑定和变异之间存在相同的旧区别......如果没有for循环,您将获得相同的行为:

# creates a `list` and binds it to name "a"
a = [1, 2, 3] 
# get the object bound to name "a" and binds it to name "b" too.
# at this point "a" and "b" both refer to the same `list` instance
b = a 
print id(a), id(b)
print a is b
# so if we mutate "a" - actually "mutate the object bound to name 'a'" - 
# we can see the effect using any name refering to this object:
a.append(42)
print b

# now we rebind "a" - make it refer to another object
a = ["a", "b", "c"]
# at this point, "b" still refer to the first list, and
# "a" refers to the new ["a", "b", "c"] list
print id(a), id(b)
print a is b
# and of course if we now mutate "a", it won't reflect on "b"
a.pop()
print a
print b

在第一回路要更改it对象(该对象的内部状态),然而,在第二循环中你重新分配it到另一个对象,留下初始对象不变。

我们来看看生成的字节码:

In [2]: def f1():
   ...:     it = range(6)
   ...:     for i in it:
   ...:         it.remove(i + 1)
   ...:         print i
   ...:         

In [3]: def f2():
   ...:     it = range(6)
   ...:     for i in it:
   ...:         it = it[:-2]
   ...:         print it
   ...:         

In [4]: import dis

In [5]: dis.dis(f1)
  2           0 LOAD_GLOBAL              0 (range)
              3 LOAD_CONST               1 (6)
              6 CALL_FUNCTION            1
              9 STORE_FAST               0 (it)

  3          12 SETUP_LOOP              36 (to 51)
             15 LOAD_FAST                0 (it)
             18 GET_ITER            
        >>   19 FOR_ITER                28 (to 50)
             22 STORE_FAST               1 (i)

  4          25 LOAD_FAST                0 (it)
             28 LOAD_ATTR                1 (remove)
             31 LOAD_FAST                1 (i)
             34 LOAD_CONST               2 (1)
             37 BINARY_ADD          
             38 CALL_FUNCTION            1
             41 POP_TOP             

  5          42 LOAD_FAST                1 (i)
             45 PRINT_ITEM          
             46 PRINT_NEWLINE       
             47 JUMP_ABSOLUTE           19
        >>   50 POP_BLOCK           
        >>   51 LOAD_CONST               0 (None)
             54 RETURN_VALUE        

In [6]: dis.dis(f2)
  2           0 LOAD_GLOBAL              0 (range)
              3 LOAD_CONST               1 (6)
              6 CALL_FUNCTION            1
              9 STORE_FAST               0 (it)

  3          12 SETUP_LOOP              29 (to 44)
             15 LOAD_FAST                0 (it)
             18 GET_ITER            
        >>   19 FOR_ITER                21 (to 43)
             22 STORE_FAST               1 (i)

  4          25 LOAD_FAST                0 (it)
             28 LOAD_CONST               2 (-2)
             31 SLICE+2             
             32 STORE_FAST               0 (it)

  5          35 LOAD_FAST                0 (it)
             38 PRINT_ITEM          
             39 PRINT_NEWLINE       
             40 JUMP_ABSOLUTE           19
        >>   43 POP_BLOCK           
        >>   44 LOAD_CONST               0 (None)

正如您所看到的, for语句使用it的可迭代( GET_ITER指令, iter(it) )。 因此,重新分配it变量不会影响循环迭代。

首先,在运行简单的for循环时,必须了解引擎盖下发生的情况,例如:

for i in it: pass

在循环开始时,创建一个迭代器 迭代器是对iter(it)的隐式调用的结果。 这是在上面的循环中引用名为it的变量的唯一时间。 参考文献中的其余部分发生时, next被称为对迭代器,但它使用了迭代器保持一个参考,而不是对象名称的对象it必然。

这对你的第二个例子意味着什么?

请注意,在你的第二个例子,你不更改列表就地,而是创建一个新的列表和变量绑定it到它。

这意味着迭代器不断引用原始列表,该列表未更改。

在第一个示例中,您可以更改原始列表,因此对next(iterator)调用将反映这些更改。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM