当前位置: 首页 > 工具软件 > Cloudopt Next > 使用案例 >

iter()和next(),__iter__和__next__

邵文乐
2023-12-01

总结

一:迭代器和可迭代对象

1:迭代器,凡是定义了__iter__和__next__方法的类都是迭代器;

2:可迭代对象,定义了__iter__或者__getitem__方法的类,叫做可迭代对象。

所以:迭代器一定是可迭代对象,但可迭代对象不一定是迭代器;

二:iter()和next()函数

iter(func)调用函数func的__iter__或者__getitem__函数,当__iter__返回的是迭代器时,则相当于iter()把一个可迭代对象转变为了迭代器,这一点后面细讲

next(迭代器)调用迭代器的__next__内置函数;

参考自:(2条消息) Python迭代器基本方法iter()及其魔法方法__iter__()原理详解_涛涛ALG的博客-CSDN博客_iter迭代器

首先,先讲一下for循环的原理,for循环会优先进到类的__iter__函数中,然后再循环调用函数的__next__方法。如果类没有定义__iter__函数,则查找类是否有__getiten__函数,如果有__getiten__,则调用默认迭代器,从0开始产生索引,来调用__getitem__方法。

像python中的list,dict等,都是可迭代对象,但不是迭代器,也就是说,list,dict这些函数是有__iter__函数的,但是没有__next__函数,但是他们的__iter__函数的返回值是一个迭代器,也就是说它们的__iter__函数的返回值是一个具有__next__函数的迭代器。


详解

一:iter()函数

参考:(24条消息) 解密 Python 迭代器的实现原理_python迭代器原理_passionSnail的博客-CSDN博客

上面说到,iter(func)会去调用func的__iter__或者__getitem__内置函数,为什么呢?我们看一下iter()的底层实现(引用自上诉参考):

static PyObject *
builtin_iter(PyObject *self, 
    PyObject *const *args, Py_ssize_t nargs)
{
    PyObject *v;
  
    // iter 函数要么接收一个参数, 要么接收两个参数
    if (!_PyArg_CheckPositional("iter", nargs, 1, 2))
        return NULL;
    v = args[0];
    // 如果接收一个参数
    // 那么直接使用 PyObject_GetIter 获取对应的迭代器即可
    // 可迭代对象的类型不同,那么得到的迭代器也不同
    if (nargs == 1)
        return PyObject_GetIter(v);
    // 如果接收的不是一个参数, 那么一定是两个参数
    // 如果是两个参数, 那么第一个参数一定是可调用对象
    if (!PyCallable_Check(v)) {
        PyErr_SetString(PyExc_TypeError,
                        "iter(v, w): v must be callable");
        return NULL;
    }
    // 获取value(哨兵)
    PyObject *sentinel = args[1];
    //调用PyCallIter_New
    //得到 calliterobject 对象
    /*
    该对象位于 Objects/iterobject.c 中
    */
    return PyCallIter_New(v, sentinel);
}

可以看到,iter()函数的输入要么只有一个,要么输入两个; 输入是一个的时候,则直接调用 PyObject_GetIter函数,否则调用PyCallIter_New函数;我们以输入一个变量调用的PyObject_GetIter函数进行说明,PyObject_GetIter函数如下:

PyObject *
PyObject_GetIter(PyObject *o)
{  
    // 获取可迭代对象的类型对象
    PyTypeObject *t = Py_TYPE(o);
    // 我们说类型对象定义的操作,决定了实例对象的行为
    // 实例对象调用的那些方法都是定义在类型对象里面的
    // 所以obj.func()本质上就是type(obj).func(obj)的语法糖
    getiterfunc f;
    // 所以这里是获取类型对象的 tp_iter 成员
    // 也就是 Python 中的 __iter__
    f = t->tp_iter;
    // 如果 f 为 NULL
    // 说明该类型对象内部的tp_iter成员被初始化为NULL
    // 即内部没有定义 __iter__ 
    // 像str、tuple、list等类型对象,它们的tp_iter成员都是不为NULL的
    if (f == NULL) {
       // 如果 tp_iter 为 NULL,那么解释器会退而求其次
       // 检测该类型对象中是否定义了 __getitem__
       // 如果定义了,那么直接调用PySeqIter_New
       // 得到一个seqiterobject对象
       // 下面的PySequence_Check负责检测类型对象是否实现了__getitem__
        if (PySequence_Check(o))
            return PySeqIter_New(o);
        // 走到这里说明该类型对象既没有__iter__、也没有__getitem__
        // 因此它的实例对象不具备可迭代的性质,于是抛出异常
        return type_error("'%.200s' object is not iterable", o);
    }
    else {
        // 否则说明定义了__iter__,于是直接进行调用
        // Py_TYPE(o)->tp_iter(o) 返回对应的迭代器
        PyObject *res = (*f)(o);
        // 但如果返回值res不为NULL、并且还不是迭代器
        // 证明 __iter__ 的返回值有问题,于是抛出异常
        if (res != NULL && !PyIter_Check(res)) {
            PyErr_Format(PyExc_TypeError,
                         "iter() returned non-iterator "
                         "of type '%.100s'",
                         Py_TYPE(res)->tp_name);
            Py_DECREF(res);
            res = NULL;
        }
        // 返回 res
        return res;
    }
}

可以看到PyObject_GetIter函数实际上就做了以下三件事:

1:判断iter()中传入的参数func是否有__iter__内置函数,如果有,直接返回这个内置参数

2:如果iter()中传入的参数func里面没有__iter__内置函数,则判断func里面是否有__getitem__函数,如果有,则返回这个__getitem__函数;

3:如果上面个两个都没有,则报错

未完待续.....

以下是目前自己的一些理解(胡说八道,便于自己理解):

iter()通过__iter__这个内置函数,将可迭代对象包装了起来,并加上了一个索引;而这个索引必须通过next()去获得,这也就是__iter__必须返回迭代器(包含__iter__和__next__函数的类就是迭代器)的原因。

通过next()获得这个索引,而这个索引指代的其实就是__next__这个内置函数,所以每一次next()相当于就是调用的__next__函数;理论上讲这个索引是没有上限的,也就是可以无限的next,但是在__next__里面可以设定终止条件;向list,tuple这些其实也是这样,内部根据自己的长度设置了终止条件

 类似资料: