可迭代对象(Iterable)与迭代器(Iterator)是python中的重要概念,两者容易被弄混淆,这里来总结一下。
for循环
我们通常用for
循环遍历一个可迭代对象,比如遍历字符串:
>>> s='abc'
>>> for i in s:
... print(i)
...
a
b
c
实际上,for
循环做了两件事:调用iter()
方法生成迭代器,以及调用next()
方法返回迭代的值。听起来可能有点费解,我们来手动调用一下:
>>> s="abc"
>>> i=iter(s) # 调用iter()函数生成迭代器
>>> type(i)
<class 'str_iterator'>
>>> next(i) # 调用next()函数,逐步返回迭代器的值
'a'
>>> next(i)
'b'
>>> next(i)
'c'
>>> next(i) # 当迭代器内部元素耗尽时,会抛出异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
我们可以看到,正如上文所说,iter()
方法生成迭代器,把迭代器传入next()
方法,返回一次迭代的值,不断重复传入就会不断的向后迭代。迭代器本身是会被“耗尽”的(exhausted),此时会抛出 ‘StopIteration’ 异常,迭代结束。
在这个例子中,字符串是可迭代对象,iter(string_obj)
则返回迭代器,后者才是真正实现迭代操作的对象。两者好像是紧密相关的,但又有所区别,下面我们分别看一下。
__iter__ 与可迭代对象
可迭代对象就是实现了__iter__
(或者__getitem__
,本文只讨论前者)魔术方法的对象,其中__iter__
返回迭代器。来看一下自己实现的可迭代对象:
class MyIterable:
def __init__(self):
self.i = MyIterator()
def __iter__(self):
return self.i
例子很简单,其中MyIterator()
是迭代器实例,下文会解释; __iter__
方法什么都不用做,只需返回一个迭代器实例。这样,我们就实现了一个可迭代对象,也可以用for 循环遍历:
>>> for word in MyIterable():
print(word)
...
This
is
a
sentence
当然,也可以手动先用Iter(MyIterable)
来构造一个迭代器,再用next()
逐步遍历。下面我们来看一下到底什么是迭代器。
__next__ 与迭代器
所谓迭代器,就是实现了__next__
魔术方法的对象,__next__
方法负责返回实际的值。当我们使用next()
方法时,__next__
会被解释器调用。我们来实现一个迭代器:
class MyIterator:
def __init__(self):
self.sentence = "This is a sentence".split()
self.index = 0
def __next__(self):
try:
word = self.sentence[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word
这里 __next__
方法一次返回句子中的一个单词。类似的,我们可以手动调用next()
方法来遍历:
>>> i = MyIterator()
>>> next(i)
'This'
>>> next(i)
'is'
>>> next(i)
'a'
>>> next(i)
'sentence'
>>> next(i)
...
StopIteration
但这时,Iterator
对象本身不能够迭代,因为它仅仅是个迭代器,不是可迭代对象:
>>> i = MyIterator()
>>> for w in i:
print(w)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'MyIterator' object is not iterable
所以为了兼容,我们可以实现__iter__
方法,其返回迭代器对象,在这里也就是返回自身:
def __iter__(self):
return self
此时我们便可以迭代MyIterator
:
>>> i = MyIterator()
>>> for w in i:
... print(w)
...
This
is
a
sentence
值得注意的是,__iter__
方法并不属于迭代器本身的内容,不实现也不会对迭代器产生影响,这里仅仅时为了与可迭代对象兼容而已。
小结
-
实际上,例子中的可迭代对象可以用更加 “Pythonic” 的方式实现:生成器,这里不再展开。总结一下生成器和迭代器:
-
- 实现
__iter__
方法的对象即为可迭代对象,该方法返回迭代器对象; - 实现
__next__
方法的对象即为迭代器,该方法实现了具体的返回值操作;为了兼容可迭代对象,也实现了__iter__
方法,返回自身即可; - 无论是
__iter__
还是__next__
,都是在执行iter()
或next()
方法时被解释器调用,for 循环实现了这个过程。
- 实现