赞
踩
总结:
一:迭代器和可迭代对象
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__函数的迭代器。
参考:(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这些其实也是这样,内部根据自己的长度设置了终止条件
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。