典型的迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import re import reprlib
RE_WORD = re.compile("\w+")
class SentenceIterator: def __init__(self, words): self.words = words self.index = 0
def __next__(self): try: word = self.words[self.index] except IndexError as e: raise StopIteration self.index += 1 return word
def __iter__(self): return self
class Sentence: def __init__(self, text): self.text = text self.word = RE_WORD.findall(text)
def __repr__(self): return f"{type(self).__name__}:{reprlib.repr(self.text)}"
def __iter__(self): return SentenceIterator(self.word)
from collections import abc print(issubclass(SentenceIterator, abc.Iterator))
|
可迭代的对象有个 __iter__ 方法,每次都实例化一个新的迭代器;而迭代器要实现 __next__ 方法,返回单个元素,此外还要实现 __iter__ 方法,返回迭代器本身。因此,迭代器可以迭代,但是可迭代的对象不是迭代器。
除了 __iter__ 方法之外,有人可能还想在Sentence类中实现 __next__ 方法,让Sentence实例既是可迭代的对象,也是自身的迭代器。可是,这种想法非常糟糕的(原因不详述)。
可迭代的对象一定不能是自身的迭代器。也就是说,可迭代的对象必须实现 __iter__ 方法,但不能实现 __next__ 方法。
生成器函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import re import reprlib
RE_WORD = re.compile("\w+")
class Sentence: def __init__(self, text): self.text = text self.word = RE_WORD.findall(text)
def __repr__(self): return f"{type(self).__name__}:{reprlib.repr(self.text)}"
def __iter__(self): for word in self.word: yield word
|
生成器函数的工作原理:
只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。
使用准确的词语描述从生成器中获取结果的过程,有助于理解生成器。注意,产出或生成值。如果说生成器“返回”值,就会让人难以理解。函数返回值;调用生成器函数返回生成器;生成器产出或生成值。生成器不会以常规的方式“返回”值:生成器函数定义体中的 return 语句会触发生成器对象抛出 StopIteration 异常。
使用 for 循环更清楚地说明了生成器函数定义体的执行过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def gen_AB(): print("start") yield "A" print("continue") yield "B" print("end")
for i in gen_AB(): print("--> ",i)
|
Sentence类升级版:惰性实现
目前实现的几版 Sentence 类都不具有惰性,因为 __init__ 方法急迫地构建好了文本中的单词列表,然后将其绑定到 self.words 属性上。这样就得处理整个文本,列表使用的内存量可能与文本本身一样多(或许更多,这取决于文本中有多少非单词字符)。如果只需迭代前几个单词,大多数工作都是白费力气。
re.finditer 函数是 re.findall 函数的惰性版本,返回的不是列表,而是一个生成器,按需生成 re.MatchObject 实例。如果有很多匹配,re.finditer 函数能节省大量内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import re import reprlib
RE_WORD = re.compile("\w+")
class Sentence: def __init__(self, text): self.text = text
def __repr__(self): return f"{type(self).__name__}:{reprlib.repr(self.text)}"
def __iter__(self): for match in RE_WORD.finditer(self.text): yield match.group()
|
Sentence类终极版:生成器表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import re import reprlib
RE_WORD = re.compile("\w+")
class Sentence: def __init__(self, text): self.text = text
def __repr__(self): return f"{type(self).__name__}:{reprlib.repr(self.text)}"
def __iter__(self): return (match.group() for match in RE_WORD.finditer(self.text))
|
和前一个示例唯一的区别是 __iter__ 方法,这里不是生成器函数了(没有 yield),而是使用生成器表达式构建生成器,然后将其返回。不过,最终的效果一样:调用 __iter__ 方法会得到一个生成器对象。
生成器表达式是语法糖:完全可以替换成生成器函数,不过有时使用生成器表达式更便利。
遇到简单的情况时,可以使用生成器表达式;如果生成器表达式要分成多行写,倾向于定义生成器函数,以便提高可读性。此外,生成器函数有名称,因此可以重用。