可迭代对象、迭代器和生成器
迭代是数据处理的基石。扫描内存中放不下的数据时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项,这就是迭代器模式。
在python语言内部,迭代器用于支持:
for 循环
构建和扩展集合类型
逐行遍历文本文件
列表推导、字典推导和集合推导
元组拆包
调用函数时,使用*拆包实参
序列可迭代的原因:iter函数
解释器需要迭代对象x时,会自动调用iter(x)
内置的iter函数由以下作用。
1、检查对象是否实现了__iter__方法,如果实现了就调用它,获取一个迭代器。
2、如果没有实现__iter__方法,但是实现了__getitem__方法,python会创建一个迭代器,尝试按顺序获取元素。
3、如果尝试失败,python抛出TypeError异常,通常会提示“C object is not iterable”
可迭代的对象与迭代器的对比
可迭代的对象:
使用iter内置函数可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的。序列都可以迭代,因为实现了__getitem__方法。
标准迭代器接口有两个方法:
__next__:返回下一个可用的元素,如果没有元素了,则抛出StopIteration异常。
__iter__:返回self,以便在应该可迭代对象的地方使用迭代器。
迭代器:
迭代器时这样的对象:实现了无参数的__next__方法,返回序列中的下一个元素,如果没有元素了,那么抛出StopIteration异常。python中的迭代器还实现了__iter__方法,因此迭代器也可以迭代。
典型的迭代器
下例中定义的Sentence类可以迭代,因为它实现了特殊的__iter__方法,构建并返回一个SentenceIterator实例。
把Sentence变成迭代器:坏主意
可迭代的对象有个__iter__方法,每次实例化一个新的迭代器,而迭代器要实现__next__方法,返回单个元素,此外还要实现__iter__方法,返回迭代器本身,因此,迭代器可以迭代,但是可迭代对象不是迭代器。
可迭代的对象一定不能是自身的迭代器。也就是说,可迭代的对象必须实现__iter__方法,但不能实现__next__方法。
生成器函数
实现相同功能,但更符合python习惯的方式是,用生成器函数代替SentenceIterator类。
在上例中,迭代器其实是生成器对象,每次调用__iter__方法都会自动创建,因为这里的__iter__方法是生成器函数。
生成器函数的工作原理
只要python函数的定义体中有yield关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象,也就是说,生成器函数是生成器工厂。
惰性实现
之前实现的几版Sentence类都不具有惰性,因为__init__方法急迫地构建好了文本中的单词列表,然后将其绑定到self.words属性上。这样就得处理整个文本,列表使用的内存量可能与文本本身一样多。
re.finditer函数是re.findall函数的惰性版本,返回的不是列表,而是一个生成器。
生成器表达式
生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。也就是说,如果列表推导是制造列表的工厂,那么生成器表达式就是制造生成器的工厂。
下例先在列表推导中使用gen_AB生成器函数,然后在生成器表达式中使用
下例用生成器表达式实现Sentence类