python的迭代器为什么一定要实现__iter__方法?

__iter()__函数只有一句话,就是return self。查了很多资料,它们关于这句话的解释就是“返回迭代器本身”。 但我想,return se…
关注者
191
被浏览
72,236

22 个回答

这是个和多态有关的问题,Python中关于迭代有两个概念,第一个是Iterable,第二个是Iterator,协议规定Iterable的__iter__方法会返回一个Iterator, Iterator的__next__方法(Python 2里是next)会返回下一个迭代对象,如果迭代结束则抛出StopIteration异常。

同时,Iterator自己也是一种Iterable,所以也需要实现Iterable的接口,也就是__iter__,这样在for当中两者都可以使用。Iterator的__iter__只需要返回自己就行了。这样,下面的代码就可以工作:

for i in my_list:
    ...

for i in iter(mylist):
    ...

for i in (v for v in mylist if v is not None):
    ...

Python中许多方法直接返回iterator,比如itertools里面的izip等方法,如果Iterator自己不是Iterable的话,就很不方便,需要先返回一个Iterable对象,再让Iterable返回Iterator。生成器表达式也是一个iterator,显然对于生成器表达式直接使用for是非常重要的。

那么为什么不只保留Iterator的接口而还需要设计Iterable呢?许多对象比如list、dict,是可以重复遍历的,甚至可以同时并发地进行遍历,通过__iter__每次返回一个独立的迭代器,就可以保证不同的迭代过程不会互相影响。而生成器表达式之类的结果往往是一次性的,不可以重复遍历,所以直接返回一个Iterator就好。让Iterator也实现Iterable的兼容就可以很灵活地选择返回哪一种。

总结来说Iterator实现的__iter__是为了兼容Iterable的接口,从而让Iterator成为Iterable的一种实现。

补充一下题主对于for的理解基本上是正确的,但仍然有一点点偏差:for为了兼容性其实有两种机制,如果对象有__iter__会使用迭代器,但是如果对象没有__iter__,但是实现了__getitem__,会改用下标迭代的方式。我们可以试一下:

>>> class NotIterable(object):
...     def __init__(self, baselist):
...         self._baselist = baselist
...     def __getitem__(self, index):
...         return self._baselist[index]
...
>>> t = NotIterable([1,2,3])
>>> for i in t:
...     print i
...
1
2
3
>>> iter(t)
<iterator object at 0x0345E3D0>

当for发现没有__iter__但是有__getitem__的时候,会从0开始依次读取相应的下标,直到发生IndexError为止,这是一种旧的迭代协议。iter方法也会处理这种情况,在不存在__iter__的时候,返回一个下标迭代的iterator对象来代替。一个重要的例子是str,字符串就是没有__iter__接口的。

__iter__

使类实例 Iterable

next()  

使类实例 是 迭代器对象

注:『迭代器对象』跟『Iterable』是两个东东哟。

题主提到,for语句来循环,它实际的工作流程是:

先判断被循环的是否是Iterable,如果不是,尽管你实现了next(),它扔不会去调用,会直接报异常



所以,题主提到的

我是这样理解的:首先调用iter获取需要迭代的对象,然后调用该对象的next方法,不知道理解对不对

是对的。只不过,python解释器是严格遵守『先调用iter来获取迭代对象,然后再调用对象next』这个步骤来的,所以,你也必须实现__iter__。

一点拙见