第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

猜你喜欢

转载自www.cnblogs.com/larken/p/9594029.html