Python如何在内部管理'for'循环?

Pau*_*pat 16 python for-loop data-structures

我正在尝试学习Python,我开始使用一些代码:

a = [3,4,5,6,7]
for b in a:
    print a
    a.pop(0)
Run Code Online (Sandbox Code Playgroud)

输出是:

[3, 4, 5, 6, 7]
[4, 5, 6, 7]
[5, 6, 7]
Run Code Online (Sandbox Code Playgroud)

我知道在我循环的时候改变数据结构并不是一个好的做法,但我想了解Python在这种情况下如何管理迭代器.

主要问题是:如果我改变状态,它如何知道它必须完成循环a

kja*_*ier 14

您不应该这样做的原因恰恰是您不必依赖迭代的实现方式.

但回到这个问题.Python中的列表是数组列表.它们表示连续的已分配内存块,而不是链接列表,其中每个元素都是独立分配的.因此,Python的列表(如C中的数组)针对随机访问进行了优化.换句话说,从元素n到元素n + 1的最有效方法是直接访问元素n + 1(通过调用mylist.__getitem__(n+1)mylist[n+1]).

因此,列表的__next__(每次迭代调用的方法)的实现就像您期望的那样:当前元素的索引首先设置为0,然后在每次迭代后增加.

在你的代码中,如果你也打印b,你会看到发生的事情:

a = [3,4,5,6,7]
for b in a:
    print a, b
    a.pop(0)
Run Code Online (Sandbox Code Playgroud)

结果:

[3, 4, 5, 6, 7] 3
[4, 5, 6, 7] 5
[5, 6, 7] 7
Run Code Online (Sandbox Code Playgroud)

因为:

  • 在迭代0 , a[0] == 3.
  • 在迭代1 , a[1] == 5.
  • 在迭代2 , a[2] == 7.
  • 在迭代3,循环结束(len(a) < 3)


Ale*_*all 9

kjaquier和Felix谈到了迭代器协议,我们可以在你的案例中看到它的实际应用:

>>> L = [1, 2, 3]
>>> iterator = iter(L)
>>> iterator
<list_iterator object at 0x101231f28>
>>> next(iterator)
1
>>> L.pop()
3
>>> L
[1, 2]
>>> next(iterator)
2
>>> next(iterator)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
StopIteration
Run Code Online (Sandbox Code Playgroud)

从这里我们可以推断出list_iterator.__next__代码的行为类似于:

if self.i < len(self.list):
    return self.list[i]
raise StopIteration
Run Code Online (Sandbox Code Playgroud)

它并不天真地获得该项目.这会引发一个IndexError泡沫到顶部:

class FakeList(object):
    def __iter__(self):
        return self

    def __next__(self):
        raise IndexError

for i in FakeList():  # Raises `IndexError` immediately with a traceback and all
    print(i)
Run Code Online (Sandbox Code Playgroud)

确实,listiter_nextCPython源代码中查看(感谢Brian Rodriguez):

if (it->it_index < PyList_GET_SIZE(seq)) {
    item = PyList_GET_ITEM(seq, it->it_index);
    ++it->it_index;
    Py_INCREF(item);
    return item;
}

Py_DECREF(seq);
it->it_seq = NULL;
return NULL;
Run Code Online (Sandbox Code Playgroud)

虽然我不知道return NULL;最终如何翻译成一个StopIteration.