# 迭代器协议

# 概述

Python的迭代器协议是一种定义了迭代器对象和容器对象之间交互的标准接口。该协议包括两个核心方法:

  1. __iter__()​:返回一个迭代器对象,该迭代器对象实现了__next__()​方法。
  2. __next__()​:返回容器中的下一个元素。当没有元素可以返回时,抛出StopIteration​异常。

容器对象必须实现__iter__()​方法,以便在请求时返回迭代器对象。迭代器对象必须实现__next__()​方法,以便在每次请求时返回容器中的下一个元素。

Python的可迭代对象都是实现了__iter__()​方法的对象,包括列表、元组、集合、字典、字符串等。通过for-in​语句或者iter()​函数来获取迭代器对象,再通过next()​函数来获取迭代器对象中的下一个元素。

迭代器协议的优点是可以通过迭代器对象实现一些高效的算法和数据处理方式,同时也使得Python的对象具有了可迭代性和可遍历性。

# 迭代器与生成器

生成器是一种特殊的迭代器,它可以用函数生成。生成器函数使用yield​关键字来产生一个值,每次调用生成器函数时,它会从上次yield语句的位置开始执行,直到遇到下一个yield语句或函数结束。

有的时候利用生成器实现迭代器协议会变得简单很多,例如树结构的遍历。

class Node:
    def __init__(self, value, children=None):
        self.value = value
        self.children = children or []

    def __iter__(self):
        return iter(self.children)

    def depth_first(self):
        yield self
        for child in self.children:
            yield from child

# 案例:边迭代边删除元素

有如下代码以及执行结果

list_ = list(range(10))
idx = 0
for i in list_:
    print(f'{idx} -- {i} -- {list_}')
    list_.pop(0)
    idx += 1

'''
0 -- 0 -- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 -- 2 -- [1, 2, 3, 4, 5, 6, 7, 8, 9]
2 -- 4 -- [2, 3, 4, 5, 6, 7, 8, 9]
3 -- 6 -- [3, 4, 5, 6, 7, 8, 9]
4 -- 8 -- [4, 5, 6, 7, 8, 9]
'''

这个结果的原因可以从list对象的迭代器源码实现中找到答案,总结下来则是:

  1. 获取迭代器时迭代器的索引会被置0,之后每次迭代索引+1
  2. 但真正获取元素时是从真正的list对象中获取
  3. 看似是在迭代元素,本质上还是在通过索引去获取元素

static PyObject *
list_iter(PyObject *seq)
{
    listiterobject *it;

    if (!PyList_Check(seq)) {
        PyErr_BadInternalCall();
        return NULL;
    }
    it = PyObject_GC_New(listiterobject, &PyListIter_Type);
    if (it == NULL)
        return NULL;
    it->it_index = 0;
    Py_INCREF(seq);
    it->it_seq = (PyListObject *)seq;
    _PyObject_GC_TRACK(it);
    return (PyObject *)it;
}


static PyObject *
listiter_next(listiterobject *it)
{
    PyListObject *seq;
    PyObject *item;

    assert(it != NULL);
    seq = it->it_seq;
    if (seq == NULL)
        return NULL;
    assert(PyList_Check(seq));

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

    it->it_seq = NULL;
    Py_DECREF(seq);
    return NULL;
}