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)
因为:
a[0] == 3.a[1] == 5.a[2] == 7.len(a) < 3)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_next在CPython源代码中查看(感谢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.
| 归档时间: |
|
| 查看次数: |
2976 次 |
| 最近记录: |