![03f7e1887182293f340969dd8c626aaf.png](https://i-blog.csdnimg.cn/blog_migrate/cc81c8db6aff51a07bf578a6027c00c3.jpeg)
有关 iterable 和 iterator 的定义介绍已经很多了:
- 实现 iter 或者 getitem 方法的对象是 iterable 对象;
- 实现 iter 和 next 方法的对象是 iterator 对象;
那为什么需要 iterator 呢?iterator 实际上是 Python 的一种 interface,它是一种遍历序列(Sequeence) 的约定,通过调用 iterator 的 next 方法实现。所谓序列:List、Tuple、Dict、Set 等等,它们的底层实现差异很大,对于某些类型的底层数据甚至是不连续的,期间参杂着一些无效值(NULL、dummy 之类)。使用 iterator 这一接口,则将序列的遍历实现推给类型自己完成,调用者只需要根据 interface 的约定,调用 next 方法即可。
List 的 next 实现
static PyObject *
listiter_next(listiterobject *it)
{
// ··· ···
// it_seq 就是迭代器的目标 list
seq = it->it_seq;
// 还有 list item 可读
if (it->it_index < PyList_GET_SIZE(seq)) {
item = PyList_GET_ITEM(seq, it->it_index);
++it->it_index;
Py_INCREF(item);
return item;
}
// list 所有成员已遍历完毕,结束
it->it_seq = NULL;
Py_DECREF(seq);
return NULL;
}
对于 List 而言,它的 items 是连续的,在 next 中通过递增 it->it_index,然后通过 PyList_GET_ITEM 读取数据。其实 List 的 next 方法是将经典的 C 语言 for 循环的计数变量放到 iterator 内部了,其实际效果与 C 下用 for 循环遍历数组差别不大。 与 Python 遍历 List 等效的 C 语言代码:
for(it->it_index = 0; it->it_index < PyList_GET_SIZE(seq), it->it_index ++){
// visit list[it->it_index]
}
Set
Dict、Set 之类容器高度依赖哈希表,其底层数据之间存在一些空缺、无效数据等,对于上层代码,如果直接遍历这些底层数据,就需要妥善处理这些特殊情况。
static int
set_next(PySetObject *so, Py_ssize_t *pos_ptr, setentry **entry_ptr)
{
// ··· ···
// 忽略那些无效值
while (i <= mask && (entry->key == NULL || entry->key == dummy)) {
i++;
entry++;
}
// ··· ···
}
在 iterator 的 next 中,自动忽略了那些无效值,上层代码可以无差别的对待 Set 之类比较特殊的数据。与 Python 遍历 Set 的等效 C 代码:
for(i = 0; i <= mask; i++, entry ++){
if(entry->key == NULL || entry->key == dummy){
continue;
}
// visit entry here
}
总结
Python 的遍历实际上与 C、Java 之类的循环方案,在形式和思想上采用了完全不同的策略,不同于 C 之类的 for 循环直接访问具体的元素,Python 的 for 循环更接近 js 的 foreach 实现,它通过约定的接口,由类型自行返回 next item。这种设计的主要目的是为了隔离不同类型底层实现的差异,否则就会出现如同上面提到的等效代码之间的差异,破坏了 Python 的动态性。
底层的 C 语言并不原生支持动态类型,Python 语言的优雅是通过大量标准接口实现的,将类型的差异约束在类型内部,对外实现标准的接口,这一点从 Python 的 PyObject 结构就可以看出来。
到此为止,我们已经将 Python 的主要类型都已涉及,明天开始我们终于可以深入 Python 的 VM 机制。需要注意的是,Python 的类型系统、内存管理同时提供了 C 和 Python 两种调用方式,在 Python VM 中我们会看到大量熟悉的类型被应用于 VM 的内部数据。Python 在整个系统的设计中,实现了相当程度的自举,Python 自身的实现也高度依赖 Python 特性,这一点非常有意思,让我们拭目以待。
关注我,了解程序员的烧脑日常,还有开源 Python 教程。
![78dd54cd30192cdb3123aad95eecc780.png](https://i-blog.csdnimg.cn/blog_migrate/862c589ac2e8e7faf109d0a43f0a5d85.jpeg)