Python中容器类的对象都是可迭代对象,外部则通过可迭代对象提供的迭代器达到逐个遍历元素的目的。
迭代器都实现了collections.abc.Iterator 基类中的两个重要方法,其中 iter() 方法用于返回迭代器本身,next() 方法用于实现逐个遍历元素的行为。
class Iterator(Iterable):
__slots__ = ()
@abstractmethod
def __next__(self):
'Return the next item from the iterator. When exhausted, raise
StopIteration'
raise StopIteration
def __iter__(self):
return self
@classmethod
def __subclasshook__(cls, C):
if cls is Iterator:
# 检查class是否实现了指定方法,issubclass(C, Iterable)返回True
return _check_methods(C, '__iter__', '__next__')
return NotImplemented
下面是一个经典的迭代器实现,用于逐个遍历一个英文句子中的单词。
import re
import reprlib
RE_WORD = re.compile(r'\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return f'Sentence({reprlib.repr(self.text)})'
def __iter__(self):
return SentenceIterator(self.words)
class SentenceIterator:
def __init__(self, words):
self.words = words
self.index = 0
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word
def __iter__(self):
return self
s = Sentence('"The time has come," the Walrus said')
for word in s:
print(word)
需要注意可迭代对象和迭代器是两个概念,实现时尽量不要混淆:
- 可迭代对象的 iter() 方法每次都返回一个全新的迭代器对象
- 具体迭代的迭代逻辑则由迭代器内部实现
这么做的原因如下。
- 支持多次独立遍历:每次对可迭代对象进行遍历(如 for word in s),都会生成一个新的迭代器对象,互不影响。如果把遍历状态(如 index)放在 Sentence 里,则同一时间内,同一个可迭代对象只能有一个遍历过程,多个遍历会互相干扰。
- 便于扩展和复用:通过将数据管理和迭代策略两项职责的分离,迭代器的实现可以灵活扩展,比如可以实现不同的遍历方式(正序、逆序、跳步等),而不需要修改可迭代对象本身。多个不同的可迭代对象也可以复用同一个迭代器实现。
- 节省内存:一个可迭代对象实例便可支持同时运行多种迭代策略。