让it
成为python中的可迭代元素。
在什么情况下,it
内部it
的变化反映了it = range(6)
for i in it:
it.remove(i+1)
print i
?或者更直接:这样的事情什么时候起作用?
it = range(6)
for i in it:
it = it[:-2]
print it
导致打印0,2,4(显示循环运行3次)。
另一方面呢
[0,1,2,3]
[0,1]
[]
[]
[]
[],
导致输出:
it = range(6)
for i in it:
it = it.remove(i+1)
print it
显示循环运行6次。我想它与就地操作或可变范围有关,但不能100%肯定地围绕它。
澄清:
一个例子,不起作用:
start_urls
导致'None'被打印并且Error(NoneType没有属性'remove')被抛出。
答案 0 :(得分:6)
当您迭代list
时,实际上会调用list.__iter__()
,这会返回绑定到listiterator
的{{1}}对象,然后实际迭代list
}。从技术上讲,这个:
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
(这使它自己对列表的引用),但变异 { {1}}显然会影响它(因为两个引用都指向同一个列表)。
嘿,重新绑定和变异之间存在同样的旧区别......没有listiterator
循环,你会得到相同的行为:
itt
答案 1 :(得分:4)
在第一个循环中,您正在更改it
对象(对象的内部状态),但是,在第二个循环中,您将it
重新分配给另一个对象,保持初始对象不变。 / p>
让我们看看生成的字节码:
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
变量不会影响循环迭代。
答案 2 :(得分:4)
首先,了解运行简单for循环时发生的事情是至关重要的,例如:
for i in it: pass
在循环开始时,会创建一个迭代器。该迭代器是对iter(it)
的隐式调用的结果。这是仅时间,在上面的循环中引用了名为it
的变量。其余的引用发生在迭代器上调用next
时,但它使用迭代器保持引用的对象,而不是名称it
绑定的对象。
这对你的第二个例子意味着什么?
请注意,在第二个示例中,您不会更改地址列表,而是创建新列表并将变量it
绑定到该列表。
这意味着迭代器不断引用原始列表,该列表未更改。
在您的第一个示例中,您更改了原始列表,因此对next(iterator)
的调用反映了这些更改。