(一)内置函数iter()返回迭代器对象
iter(object[, sentinel])——返回一个iterator对象
- 如果没有给定第二个参数sendinel,则第一个参数object必须是支持迭代协议(__iter __()方法)的集合对象,或者它必须支持序列协议(整数参数从0开始的__getitem __()方法)。 如果它不支持这些协议中的任何一个,则会引发TypeError。 (下面主要围绕这种情况展开)
- 如果给定第二个参数sendinel,则第一个参数object必须是可调用对象。 此时,iter 创建了一个迭代器对象,每次调用这个迭代器对象的__next__()方法时,都会调用 object(可调用对象)。 如果返回的值等于sendinel,则将引发StopIteration,否则将返回该值。
内置函数iter()源码如下,也就是获取object参数的迭代器对象是通过PyObject_GetIter方法获得的。
static PyObject *builtin_iter(PyObject *self, PyObject *args)
{
PyObject *v, *w = NULL;
if (!PyArg_UnpackTuple(args, "iter", 1, 2, &v, &w)) //[1]传入的参数args是元组,长度min=1,max=2,args[0]填充变量v,arg[1]填充变量w
return NULL;
if (w == NULL)
return PyObject_GetIter(v); //[2]arg[0]是iter(object[, sentinel])中的object参数
if (!PyCallable_Check(v)) {
PyErr_SetString(PyExc_TypeError,
"iter(v, w): v must be callable");
return NULL;
}
return PyCallIter_New(v, w);
}
PyArg_UnpackTuple用法说明:docs.python.org/2/c-api/arg…
(二)for循环如何获得对象的迭代器
由for循环的[GET_ITER]代码段可知,**遍历过程中通过PyObject_GetIter方法获取迭代器对象,和iter()内置函数使用的是一样的方法。**下面是PyObject_GetIter方法代码:
PyObject* PyObject_GetIter(PyObject *o){
PyTypeObject *t=o->ob_type;
getiterfunc f=NULL;
if(PyType_HasFeature(t,PY_TPFLAGS_HAVE_ITER))
//获得类型对象中的tp_iter操作
f=t->tp_iter;
if(f==NULL){
...
}else{
//通过tp_iter操作获得iterator
PyObject *res=(*f)(o);
...
return res;
}
}
从上面代码可以看出,PyObject_GetIter方法是通过调用对象(比如PyLisyObject)对应的的类型对象(比如PyList_Type)的tp_iter域获取迭代器的,类型对象PyList_Type的tp_iter域被设置成list_iter方法(juejin.cn/post/686413…)。当我们自定义迭代器对象时,定义__iter__()方法,实则就是将tp_iter域设置成__iter__()。
(三)定义迭代器(iterator)对象
迭代器对象需要实现迭代器协议的两个方法
iterator.__iter__()
:返回迭代器对象本身。
这个方法是使用for和in语句遍历容器或迭代器对象所必需的, 它对应于Python / C API中Python对象的类型结构的tp_iter插槽。(对于列表,tp_iter设置为list_iter。)
iterator.__next__()
:返回容器的下一个元素,如果没有,则引发StopIteration异常
** 这个方法对应于Python / C API中Python对象的类型结构的tp_iternext插槽。**(对于列表,p_iternext设置为listiter_next。)
这两个方法,就是整个for循环的关键方法,以下等同:
lst=[1,2,3,4,5]
for x in lst:
print(x)
lst=[1,2,3,4,5]
it=iter(lst)
while True:
try:
x=next(it)
print(x)
except StopIteration:
break
(四)如何定义能够被for循环遍历的对象
for和in语句和iter()都用同样的方法获取迭代器,所以可以认为入参也相同。也就是,for循环可以遍历的是支持迭代协议(__iter __()
方法)的集合对象,或者它必须支持序列协议(整数参数从0开始的__getitem __()
方法)的对象——可迭代(iterable)对象。
可迭代对象:docs.python.org/3/glossary.…
(1)支持迭代协议:生成器(generators)、file objects、集合、dict.keys(), dict.values() and dict.items();
(2)支持序列协议:列表、元组、字符串、range
(3)自定义对象:实现可迭代协议(__iter __()
方法)或序列协议(__getitem __()
方法)
在定义对象时定义__iter__()
就可实现可迭代对象。但是要能被for和in语句遍历。
1、__iter__()
方法返回一个包含__next__()
方法的对象
2、__iter__()
方法返回self,同时定义__next__()
方法。也就是对象本身既是可迭代对象也是迭代器。
对于方法2,如果不定义__next__()
方法,在调用iter()方法/for循环获取迭代器会报错“TypeError: iter() returned non-iterator of type '类名‘”,我觉得这里应该和PyObject_GetIter方法中"PyType_HasFeature(t,PY_TPFLAGS_HAVE_ITER)"这段代码有关。
- 方法1实现for循环遍历对象
TraverseIterator有__iter__()
方法时,print(isinstance(TraverseIterator(self.data), Iterator))
输出结果是True,否则输出结果时False。
不过即便没有TraverseIterator没有实现__iter__()
方法,也就是没有注释[2]处的代码,依然可以被for循环所遍历。
- 方法2实现for循环遍历对象: