第14章 可迭代的对象、迭代器和生成器
#第14章 可迭代的对象、迭代器和生成器 # 迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。 # 所有生成器都是迭代器,因为生成器完全实现了迭代器接口。 # 迭代器用于从集合中取出元素;而生成器用于“凭空”生成元素。 # 14.1 Sentence类第1版:单词序列 # 示例 14-1 sentence.py:把句子划分为单词序列 import re import reprlib RE_WORD=re.compile('\w+') class Sentence: def __init__(self,text): self.text=text self.words=RE_WORD.findall(text) def __getitem__(self, index): return self.words[index] def __len__(self):# 为了完善序列协议,我们实现了__len__方法;不过,为了让对象可以迭代,没必要实现这个方法。 return len(self.words) def __repr__(self):# reprlib.repr 这个实用函数用于生成大型数据结构的简略字符串表示形式。 return 'Sentence(%s)'%reprlib.repr(self.text) # 示例 14-2 测试 Sentence 实例能否迭代 s=Sentence('"The time has come,"the Walrus said,') print(s) for word in s: print(word) print(list(s)) print(s[0],s[5],s[-1]) # 任何 Python 序列都可迭代的原因是,它们都实现了 __getitem__ 方法。 # 从Python 3.4开始,检查对象x能否迭代,最准确的方法是:调用iter(x)函数,如果不可迭代,再处理TypeError异常。 # 14.2 可迭代的对象与迭代器的对比 # 标准的迭代器接口有两个方法。 # __next__返回下一个可用的元素,如果没有元素了,抛出 StopIteration异常。 # __iter__返回 self,以便在应该使用可迭代对象的地方使用迭代器,例如在for循环中。 # 迭代器迭代器是这样的对象:实现了无参数的 __next__ 方法,返回序列中的下一个元素;如果没有元素了,那么抛出 StopIteration 异常。 # 14.3 Sentence类第2版:典型的迭代器 # 例14-4 sentence_iter.py:使用迭代器模式实现Sentence类 import re import reprlib RE_WORD=re.compile('\w+') class Sentence: def __init__(self,text): self.text=text self.words=RE_WORD.findall(text) def __repr__(self): return 'Sentence(%s)'%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 # 迭代器模式可用来: # 访问一个聚合对象的内容而无需暴露它的内部 # 表示支持对聚合对象的多种遍历 # 为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代) # 14.4 Sentence类第3版:生成器函数 import re import reprlib RE_WORD=re.compile('\w+') class Sentence: def __init__(self,text): self.text=text self.words=RE_WORD.findall(text) def __repr__(self): return 'Sentence(%s)'.reprlib.repr(self.text) def __iter__(self): for word in self.words: yield word return # 这个return语句不是必要的;这个函数可以直接“落空”,自动返回。 # 不管有没有return语句,生成器函数都不会抛出StopIteration异常,而是在生成完全部值之后会直接退出。 # 生成器函数的工作原理 # 只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函数。 # 调用生成器函数时,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。 # 只要 Python 函数中包含关键字 yield,该函数就是生成器函数。 # 14.5 Sentence类第4版:惰性实现 # 示例 14-7 sentence_gen2.py: 在生成器函数中调用 re.finditer生成器函数,实现 Sentence 类 import re import reprlib RE_WORD=re.compile('\w+') class Sentence: def __init__(self,text): self.text=text def __repr__(self): return 'Sentence(%s)'%reprlib.repr(self.text) def __iter__(self): for match in RE_WORD.finditer(self.text) yield match.group() # 14.6 Sentence类第5版:生成器表达式 # 生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。 # 示例 14-9 sentence_genexp.py:使用生成器表达式实现 Sentence类 import re import reprlib RE_WORD=re.compile('\w+') class Sentence: def __init__(self,text): self.text=text def __repr__(self): return 'Sentence(%)'%.reprlib.repr(self.text) def __iter__(self): return (match.group() for match in RE_WORD.finditer(self.text)) # 生成器表达式是语法糖:完全可以替换成生成器函数,不过有时使用生成器表达式更便利。下一节说明生成器表达式的用途。 # 14.7 何时使用生成器表达式 # 生成器表达式是创建生成器的简洁句法,这样无需先定义函数再调用。 # 不过,生成器函数灵活得多,可以使用多个语句实现复杂的逻辑,也可以作为协程使用 # 14.8 另一个示例:等差数列生成器 # 典型的迭代器模式作用很简单——遍历数据结构。 # 示例 14-11 ArithmeticProgression 类 class ArithmeticProgression: def __init__(self,begin,step,end=None): self.begin=begin self.step=step self.end=end def __iter__(self): result=type(self.begin+self.step)(self.begin) forever=self.end is None index=0 while forever or result <self.end: yield result index +=1 result=self.begin+self.step*index # 示例 14-10 演示 ArithmeticProgression 类的用法 ap=ArithmeticProgression(0,1,3) print(list(ap)) ap=ArithmeticProgression(1,.5,3) print(list(ap)) ap=ArithmeticProgression(0,1/3,1) print(list(ap)) from fractions import Fraction ap=ArithmeticProgression(0,Fraction(1,3),1) print(list(ap)) from decimal import Decimal ap=ArithmeticProgression(0,Decimal('.1'),.3) print(list(ap)) # 使用itertools模块生成等差数列 # Python3.4中的itertools模块提供了19个生成器函数,结合起来使用能实现很多有趣的用法。 import itertools gen=itertools.count(1,.5) print(next(gen),next(gen),next(gen)) # 不过,itertools.takewhile 函数则不同,它会生成一个使用另一个生成器的生成器, # 在指定的条件计算结果为 False 时停止。因此,可以把这两个函数结合在一起使用。 gen=itertools.takewhile(lambda n:n<3,itertools.count(1,.5)) print(list(gen)) # 示例 14-13 aritprog_v3.py:与前面的aritprog_gen函数作用相同 import itertools def aritprog_gen(begin,step,end=None): first=type(begin+step)(begin) ap_gen=itertools.count(first,step) if end is not None: ap_gen=itertools.takewhile(lambda n:n<end,ap_gen) return ap_gen # 示例 14-13 想表达的观点是,实现生成器时要知道标准库中有什么可用,否则很可能会重新发明轮子。 # 14.9 标准库中的生成器函数 # 示例14-15演示itertools.accumulate生成器函数 sample=[5,4,2,8,7,6,3,0,9,1] import itertools print(list(itertools.accumulate(sample)))#计算总和。 print(list(itertools.accumulate(sample,min)))#计算最小值。 print(list(itertools.accumulate(sample,max)))#计算最大值。 import operator print(list(itertools.accumulate(sample,operator.mul)))#计算乘积。 print(list(itertools.accumulate(range(1,11),operator.mul)))#从1!到10!,计算各个数的阶乘。 # 示例 14-17 演示用于合并的生成器函数 print(list(itertools.chain('ABC',range(2)))) print(list(itertools.chain(enumerate('ABC')))) print(list(itertools.chain.from_iterable(enumerate('ABC')))) print(list(zip('ABC',range(5)))) print(list(zip('ABC',range(5),[10,20,30,40]))) print(list(itertools.zip_longest('ABC',range(5)))) print(list(itertools.zip_longest('ABC',range(5),fillvalue='?'))) #14.10 Python3.3中新出现的句法:yield from def chain(*iterables): for i in iterables: yield from i print(list(chain('ABC',tuple(range(3))))) # 除了代替循环之外,yield from还会创建通道,把内层生成器直接与外层生成器的客户端联系起来。 # 14.11 可迭代的归约函数 # 表 14-6 中的函数都接受一个可迭代的对象,然后返回单个结果。这些函数叫“归约”函数、“合拢”函数或“累加”函数。 # 此外,对all和any数来说,有一项重要的优化措施是reduce函数做不到的: # 这两个函数会短路(即一旦确定了结果就立即停止使用迭代器)。 # all(it)it 中的所有元素都为真值时返回True,否则返回False;all([])返回True # any(it)只要it中有元素为真值就返回True,否则返回False;any([])返回False # 14.12 深入分析iter函数 from random import randint def d6(): return randint(1,6) d6_iter=iter(d6,1) # 第二个值是哨符,这是个标记值,当可调用的对象返回这个值时, # 触发迭代器抛出 StopIteration 异常,而不产出哨符。 print(d6_iter) for roll in d6_iter: print(roll) # 14.13 案例分析:在数据库转换工具中使用生成器 # 14.14 把生成器当成协程 # 14.15 本章小结 # 14.16 延伸阅读 def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b