当前位置:   article > 正文

iter()和next(),__iter__和__next__

__iter__和__next__

总结

一:迭代器和可迭代对象

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()的底层实现(引用自上诉参考):

  1. static PyObject *
  2. builtin_iter(PyObject *self,
  3. PyObject *const *args, Py_ssize_t nargs)
  4. {
  5. PyObject *v;
  6. // iter 函数要么接收一个参数, 要么接收两个参数
  7. if (!_PyArg_CheckPositional("iter", nargs, 1, 2))
  8. return NULL;
  9. v = args[0];
  10. // 如果接收一个参数
  11. // 那么直接使用 PyObject_GetIter 获取对应的迭代器即可
  12. // 可迭代对象的类型不同,那么得到的迭代器也不同
  13. if (nargs == 1)
  14. return PyObject_GetIter(v);
  15. // 如果接收的不是一个参数, 那么一定是两个参数
  16. // 如果是两个参数, 那么第一个参数一定是可调用对象
  17. if (!PyCallable_Check(v)) {
  18. PyErr_SetString(PyExc_TypeError,
  19. "iter(v, w): v must be callable");
  20. return NULL;
  21. }
  22. // 获取value(哨兵)
  23. PyObject *sentinel = args[1];
  24. //调用PyCallIter_New
  25. //得到 calliterobject 对象
  26. /*
  27. 该对象位于 Objects/iterobject.c 中
  28. */
  29. return PyCallIter_New(v, sentinel);
  30. }

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

  1. PyObject *
  2. PyObject_GetIter(PyObject *o)
  3. {
  4. // 获取可迭代对象的类型对象
  5. PyTypeObject *t = Py_TYPE(o);
  6. // 我们说类型对象定义的操作,决定了实例对象的行为
  7. // 实例对象调用的那些方法都是定义在类型对象里面的
  8. // 所以obj.func()本质上就是type(obj).func(obj)的语法糖
  9. getiterfunc f;
  10. // 所以这里是获取类型对象的 tp_iter 成员
  11. // 也就是 Python 中的 __iter__
  12. f = t->tp_iter;
  13. // 如果 f 为 NULL
  14. // 说明该类型对象内部的tp_iter成员被初始化为NULL
  15. // 即内部没有定义 __iter__
  16. // 像strtuplelist等类型对象,它们的tp_iter成员都是不为NULL的
  17. if (f == NULL) {
  18. // 如果 tp_iter 为 NULL,那么解释器会退而求其次
  19. // 检测该类型对象中是否定义了 __getitem__
  20. // 如果定义了,那么直接调用PySeqIter_New
  21. // 得到一个seqiterobject对象
  22. // 下面的PySequence_Check负责检测类型对象是否实现了__getitem__
  23. if (PySequence_Check(o))
  24. return PySeqIter_New(o);
  25. // 走到这里说明该类型对象既没有__iter__、也没有__getitem__
  26. // 因此它的实例对象不具备可迭代的性质,于是抛出异常
  27. return type_error("'%.200s' object is not iterable", o);
  28. }
  29. else {
  30. // 否则说明定义了__iter__,于是直接进行调用
  31. // Py_TYPE(o)->tp_iter(o) 返回对应的迭代器
  32. PyObject *res = (*f)(o);
  33. // 但如果返回值res不为NULL、并且还不是迭代器
  34. // 证明 __iter__ 的返回值有问题,于是抛出异常
  35. if (res != NULL && !PyIter_Check(res)) {
  36. PyErr_Format(PyExc_TypeError,
  37. "iter() returned non-iterator "
  38. "of type '%.100s'",
  39. Py_TYPE(res)->tp_name);
  40. Py_DECREF(res);
  41. res = NULL;
  42. }
  43. // 返回 res
  44. return res;
  45. }
  46. }

可以看到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这些其实也是这样,内部根据自己的长度设置了终止条件

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/145465
推荐阅读
  

闽ICP备14008679号