6.可迭代的对象、迭代器和生成器
迭代是数据处理的基石。扫描内存中方不下的数据集时,我们要找一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式。
6.1可迭代对象
6.1.1序列可以迭代的原因
解释器需要迭代对象x时,会自动调用iter(x),内置的iter函数有以下作用:
检查对象是否实现了__iter__方法,如果实现了就调用它,获取一个迭代器。
如果没有实现__iter__方法,但是实现了__getitem__方法,python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素
如果尝试失败,python抛出TypeError异常,通常会提示”C object is not iterable“,其中C是目标迭代对象
6.2 迭代器
6.2.1迭代器的接口
标准的迭代器接口有两个方法:
__next__:返回下一个可用的元素,如果没有元素了,抛出StopIteration
__iter__:返回self,以便在应该使用可迭代对象的地方使用迭代器,例如在for循环中
6.2.2 一个经典的迭代器
#可迭代对象和迭代器一定不能在一个对象中同时实现,一下为典型的迭代器
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 __iter__(self):
return SentenceIterator(self.words)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
#实现迭代器
class SentenceIterator(self,words):
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
6.2.3为什么不能把迭代对象同时变成迭代器(添加__next__()方法)
《设计模式:可复用面向对象软件的基础》一书认为:
迭代器模式可以用来:
访问一个聚合对象的内容而无需暴露它的内部表示
支持对聚合对象的多种遍历
为遍历不同的聚合结构提供统一的接口(即支持多态迭代)
为了”支持多种遍历“,必须能从同一个可迭代的实例中获取多个独立的迭代器,而且各个迭代器要能维护自身的内部状态,因此这一模式正确的实现方式是,每次调用iter(my_iterable)都新建一个独立的迭代器,这就是为什么可迭代对象一定不能是自身的迭代器。也就是说,可迭代的对象必须实现 __iter__方法,但不能实现__next__方法
6.2.4 用生成器函数代替迭代器
只要python函数的定义体中有yield关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象,也就是说,生成器函数就是生成器工厂。
#使用生成器yield代替SentenceIterator类
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 __iter__(self):
for word in self.words:
yield word
return
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
6.2.5 使用生成器表达式改进生成器函数,并且惰性实现
#使用生成器表达式简化生成器函数,并且对__iter__函数惰性实现
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self,text):
self.text = text
def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
6.3 标准库中的生成器函数
6.3.1 过滤功能
从输入的可迭代对象中产出元素的子集,而且不修改元素本身
模块 | 函数 | 说明 |
---|---|---|
itertools | compress(it, selector_it) | 并行处理两个可迭代的对象;如果selector_it中的元素是真值,产出it中对应的元素 |
itertools | dropwhile(predicate,it) | 处理it,跳过predictate的计算结果为真值的元素,然后产出剩下的各个元素(从第一个False值之后不再进一步检查) |
(内置) | filter(predicate,it) | 把it中的各个元素传给predicate,如果predicate(item)返回真值,那么产出对应的元素,如果predicate是None,那么只产出对应的元素 |
itertools | filterfalse(predicate,it) | 与filter函数的作用类似,不过predicate的逻辑是相反的;predicate返回假值时产出对应的元素 |
itertools | islice(it, stop)或isclice(it,start,stop,step=1) | 产出it的切片,作用类似于s[:stop]或s[start:stop:step],不过it可以是任何可迭代的对象,而且这个函数实现的是惰性操作 |
itertools | takewhile(predicate, it) | predicate返回真值时产出对应的元素,然后立即停止,不再继续检查 |
6.3.2用于映射的生成器函数
模块 | 函数 | 说明 |
---|---|---|
itertools | accumulate(it, [func]) | 产出累计的总和;如果提供了func,那么把前两个元素传给它,然后把计算结果和下一个元素传给它,一次类推,最后产出结果 |
(内置) | enumerate(iterable,start =0) | 产出由两个元素组成的元祖,结构是(index, item),其中index从startt开始计数,item则从iterable中获取 |
(内置) | map(func,it1,[it2,...,itN]) | 把it中的各个元素传给func,产出结果;如果传入N个可迭代的对象,那么func必须能接受N个参数,而且要并行处理各个可迭代对象 |
itertools | starmap(func, it) | 把it中的各个元素传给func,产出结果;输入的可迭代对象应该产出可迭代的元素iit,然后func(*iit)这种形式调用func |
6.3.3合并多个可迭代对象的生成器函数
模块 | 函数 | 说明 |
---|---|---|
itertools | chain(it1,...,itN) | 先产出it1中的所有元素,然后产出it2中的所有元素,一次类推,无缝连接在一起 |
itertools | chain.from_iterable(it) | 产出it生成的各个可迭代对象的元素,一个接一个,无缝连接在一起;it应该产出可迭代的元素,例如可迭代的对象列表 |
itertools | product(it1,...,itN,repeat=1) | 计算笛卡尔积:从输入的各个可迭代对象中获取元素,合并成由N个元素组成的元祖,与嵌套的for循环效果一样;repeat指明重复处理多少次输入的可迭代对象 |
(内置) | zip(it1,...,itN) | 并行从输入的各个可迭代对象中获取元素,产出由N个元素组成的元祖,只要有一个可迭代的对象到头了,就默默地停止 |
itertools | zip_longest(it1,...,itN,fillvalue=None) | 并行从输入的各个可迭代对象中获取元素,产出由N个元素组成的元祖,等到最长的可迭代对象到头后才停止,空缺的值使用fillvalue填充 |
6.3.4把输入的各个元素扩展成多个输出元素的生成器函数
模块 | 函数 | 说明 |
---|---|---|
itertools | combinations(it, out_len) | 把it产出的out_len个元素组合在一起,然后产出 |
itertools | combinations_with_replacement(it, out_len) | 把it产出的out_len个元素组合在一起,然后产出,包含相同的元素组合 |
itertools | count(start = 0, step =1) | 从start开始不断产出数字,按step指定的步幅增加 |
itertools | cycle(it) | 从it中产出各个元素,存储各个元素的副本,然后按顺序重复不断的产出各个元素 |
itertools | permutations(it, out_len=None) | 把out_len个it产出的元素排列在一起,然后产出这些排列;out_len的默认值等于len(list(it)) |
itertools | repeat(item, [items]) | 重复不断产出指定的元素,除非提供times,指定次数 |
6.3.5 用于重新排列元素的生成器函数
模块 | 函数 | 说明 |
---|---|---|
itertools | groupby(it, key=None) | 产出由两个元素组成的元素,形式为(key, group),其中key是分组标准,group是生成器,用于产出分组中的元素 |
(内置) | reversed(seq) | 从后向前,倒序产出seq中的元素,seq必须是序列,或者是实现了__reversed__特殊方法的对象 |
itertools | tee(it, n=2) | 产出一个由n个生成器组成的元祖,每个生成器用于单独产出输入的可迭代对象中的元素 |
6.3.6可迭代的规约函数
模块 | 函数 | 说明 |
---|---|---|
(内置) | all(it) | it中的所有元素都为真值时返回True,否则返回False,all([])返回True |
(内置) | any(it) | 只要it中的元素为真值就返回True,否则返回False,any([])返回false |
(内置) | max(it, [key=,][default=]) | 返回it中最大的元素,*key是排序函数,与sorted函数中的一样,如果可迭代的对象为空,返回default |
(内置) | min(it, [key=,][default=]) | 返回it中最小的元素,*key是排序函数,与sorted函数中的一样,如果可迭代的对象为空,返回default |
functools | reduce(func,it,[initial]) | 把前两个元素传给func,然后把计算结果和第三个元素传给func,以此类推,返回最后的结果,如果提供了initial,把它当作第一个元素传入 |
(内置) | sum(it,start=0) | it中的所有元素的总和,如果提供可选的start,会把它加上(计算浮点数的加法时,可以使用math.fsum函数提高精度) |