1.Sentence类第1版:单词序列
迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据的方式,即按需一次获取一个数据项。这就是迭代器模式。
我们从下面的例子中开始对迭代器进行叙述。
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):
return len(self.words)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
上段代码中,re.findall返回一个字符串列表,里面的元素都是正则表达式的全部非重叠匹配。将其返回的结果保存在self.words中。最后,reprlib.repr这个实用函数用于生成大型数据结构的简略字符串表示形式。
1.1序列可以迭代的原因:iter函数
解释器需要迭代对象x时,会自动调用iter(x)。内置的iter函数有以下作用:
(1)检查对象是否实现了iter方法,如果实现了就调用它,获取一个迭代器。
(2)如果没有实现iter方法,但是实现了getitem方法,Python会创建一个迭代器,尝试按顺序获取元素。
(3)如果尝试失败,会抛出TypeError。
2.可迭代的对象与迭代器的对比
可迭代对象:使用iter内置函数可以获取迭代器的对象。如果对象实现了能返回会迭代器的iter方法,那么对象就是可迭代的。序列都可以迭代,实现了getitem方法,而且其参数是从零开始的索引,这种对象就是可迭代的。
迭代器:实现了无参数的_next_方法,返回序列中的下一个元素,如果没有元素了,那么就抛出StopIteration异常。Python中的迭代器还实现了_iter_方法,因此迭代器是可以迭代的。
并且,我们要明确可迭代的对象和迭代器之间的关系,Python从可迭代的对象中获取迭代器。标准的迭代器接口包含两个方法。
(1)_next_:返回下一个可用的元素,如果没有元素了,就抛出StopIteration异常。
(2)_iter_:返回self,以便在应该使用可迭代对象的地方使用迭代器,例如在for循环中。
2.1把Sentence变成迭代器:坏主意
构建可迭代的对象和迭代器时经常会出现错误,原因是混淆了二者。要知道,可迭代的对象有个_iter_方法,每次都实例化了一个新的迭代器。而迭代器要实现next方法,返回单个元素,此外,还要实现_iter_方法,返回实例本身。
因此,迭代器可以迭代,但是可迭代的对象不是迭代器。
3.Sentence第2版:生成器函数
先使用生成器函数编写的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):
for word in self.words:
yield word
return
该段代码,在iter方法中,迭代self.words,并产出当前的word。要注意,return语句并不是必要的,这个函数可以直接落空,自动返回。
在Python中函数定义体中有yield的关键字,该函数就是生成器函数。调用生成器函数,就会返回一个生成器对象。也就是说,生成器函数就是生成器工厂。生成器函数会创建一个生成器对象,包装生成器函数的定义体。把生成器传给next()函数时,生成器函数会向前,执行函数定义体的下一个yield语句,返回产出值,并在函数定义体的当前位置暂停。最终,函数的定义体返回时,外层的生成器对象会抛出StopIteration异常。
4.标准库中的生成器函数
标准库中提供了很多生成器,下面介绍一下常用的生成器函数。
4.1用于过滤的生成器函数
从输入的可迭代对象中产出元素的子集,而且不修改元素本身。
模块 | 函数 | 说明 |
itertools | compress(it,selector_it) | 并行处理讲个可迭代的对象,如果selector_it中的元素是真值,产出it中对应的元素。 |
itertools | dropwhile(predicate,it) | 处理it,跳过predicate的计算结果为真值的元素,然后产出剩下的各个元素。 |
内置 | filter(predicate,it) | 把it中的各个元素传给predicate,如果返回真值,就产出对应的元素,如果为None,那么只产出真值元素。 |
itertools | filterfalse(predicate,it) | 把it中的各个元素传给predicate,如果返回假值,就产出对应的元素。与filter相反。 |
itertools | islice(it,stop) | 产出it的切片,作用类似于s[:stop],并且这个函数实现的是惰性操作。 |
itertools | takewhile(predicate,it) | predicate返回真值时产出对应的元素,然后立即停止,不再继续检查。 |
4.2用于映射的生成器函数
在输入的单个可迭代对象中的各个元素上做计算,然后返回结果。如果输入来自多个可迭代的对象,第一个可迭代对象到头后就停止输出。
模块 | 函数 | 说明 |
itertools | accumulate(it,[func]) | 产出累积的总和;如果提供了func,那么把前两个元素传给它,然后把计算结果和下一个元素传给它,以此类推,产出最后结果。 |
内置 | enumerate(iterable,start=0) | 产出的两个元素组成的元组,结构是(index, item) ,其中index从start开始计数,item从iterable获取。 |
内置 | map(func,it1,[it2,…,itN]) | 把it中的各个元素传给func,产出结果,如果传入N个可迭代的对象,那么func必须可以接受N个参数,而且要并行处理各个可迭代的的对象。 |
itertools | starmap(func,it) | 把it中的各个元素传给func,产出结果,输入的可迭代对象应该产出可迭代的元素iit,然后以func(*iit) 这种形式调用func。 |
4.3用于合并的生成器函数
这些函数都从输入的可迭代对象中产出元素。chain和chain.form_iterable按顺序(一个接一个)处理输入的可迭代对象,而product、zip和zip_longest并行处理输入的各个可迭代对象。
模块 | 函数 | 说明 |
itertools | chain(it1,…,itN) | 先产出it1中的元素,然后产出it2中的元素,以此类推,无缝衔接在一起。 |
itertools | chain.form_iterable(it) | 产出it生成的各个可迭代对象中的元素,一个接一个无缝衔接在一起,it应该产出可迭代的元素,例如可迭代的对象列表。 |
itertools | product(it1,…,itN,repeat=1) | 计算笛卡尔积:从输入的各个可迭代对象中获取元素,与嵌套的for循环一样;repeat指明重复处理多少次输入的可迭代对象。 |
内置 | zip(it1,…,itN) | 并行从输入的各个可迭代对象中获取元素,产出由N个元素组成的元组,只要有一个可迭代对象那个到头了,就默默停止。 |
itertools | zip_longest(it1,…,itN,fillvalue=None) | 并行从输入的各个可迭代对象中获取元素,产出由N个元素组成的元组,等到最长的可迭代对象到头后才停止,空缺得到值使用fillvalue来填充。 |
4.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=Noine) | 把out_len个it产出的元素排列在一起,然后产出这些排列,out_len的默认值等于len(list(it))。 |
itertools | repeat(item,[times]) | 重复不断地产出指定元素,除非提供times,指定次数。 |
4.5用于重新排列的生成器函数
有些函数生成器用于产出输入的可迭代对象中的全部元素,不过会以某种方式重新排列。其中,有两个函数会返回多个生成器,分别是itertools.groupby和itertools.tee。这一组里的另一个函数生成器。内置的reversed函数,是本节所述函数中唯一一个不接受可迭代的对象,而只接受序列为参数的函数。
模块 | 函数 | 说明 |
itertools | groupby(it,key=None) | 产出由两个元素组成的元素,形式为(key,group),其中,key为分组标准,group是生成器,用于产出分组里的元素。 |
内置 | reversed(seq) | 从后向前,倒序产出seq中的元素,seq必须是序列,或者是实现了_reverse_特殊方法的对象。 |
itertools | tee(it,n=2) | 产出一个有n个生成器组成的元组,每个生成器用于单独产出输入的可迭代对象中的元素。 |
4.6可迭代的归约函数
下面表格中的函数都接受一个可迭代的对象,然后返回单个结果,这些函数叫做归约函数。
模块 | 函数 | 说明 |
内置 | all(it) | it中都为真值时返回Ture,否则返回False。all([])返回Ture。 |
内置 | any(it) | 只要it中有元素为真值就返回Ture,否则返回False。all([])返回False。 |
内置 | max(it, [key=,] [default=]) | 返回it中值最大的元素:key为排序函数,与sorted函数一样,如果可迭代对象为空,返回default。 |
内置 | min(it, [key=,] [default=]) | 返回it中值最小的元素:key为排序函数,与sorted函数一样,如果可迭代对象为空,返回default。 |
functools | reduce(func, it, [inital]) | 把前两个元素传给func,然后把计算的结果和第三个元素传给func,以此类推,返回最后的结果。如果提供了initial,把它当做第一个元素传入。 |
内置 | sum(it, start=0) | it中所有元素的总和,如果提供了start会把它加上。 |