可迭代对象、迭代器、生成器

想说一句题外话,很长一段时间我都没弄懂迭代跟遍历的关系

后来前辈跟我:遍历是目的,迭代是手段,我才恍然大悟,哈哈哈哈哈


1, 可迭代对象

所有Python 程序员都知道,序列可以迭代, 下面说明具体原因:

(1)检查对象是否实现了__iter__方法,如果实现了就调用它,获取一个迭代器(什么是迭代器,下面会讲)。

(2)如果没有实现__iter__方法,但实现了__getitem__方法,Python会创建一个迭代器,尝试按顺序

(从索引0开始)获取元素。

(3)如果尝试失败,Python跑出TpyeError异常,通常会提示“C object is not iterable”(C 对象不可迭代),

其中C 是目标对象所属的类。

任何Python序列都可迭代的原因是,它们都实现了__getitem__方法。其实,标准的序列也都实现了__iter__方法。


在从Python3.4开始,检查对象x能否迭代,最准确的方法是:调用iter(x)函数,如果不可迭代,在处理TypeError异常。

这是比使用isinstance(x, abc.Iteradble)更准确, 因为iter(x) 函数会考虑到遗留的__getitem__方法,

而abc.Iterable类则不考虑。


可迭代的对象:

使用iter 内置函数可以获取迭代器的对象。如果对象实现了能返 回迭代器的__iter__ 方法,那么对象就是可迭代的。

序列都可以迭 代;实现了__getitem__ 方法,而且其参数是从零开始的索引,这种 对象也可以迭代。


2,迭代器



创建一个迭代器


我们要明确可迭代的对象和迭代器之间的关系:Python从可迭代的对象中获取迭代器。

下面是一个简单的 for 循环,迭代一个字符串。这里,字符串 'ABC' 是可迭代的对象。背后是有迭代器的,只不过我们看不到:


如果没有for语句,就必须要用while 循环模拟,要写成想下面这样:


(1)使用可迭代的对象构建迭代器it。

(2)不断在迭代器上调用next函数,或许下一个字符。

(3)如果没有字符了,迭代器会跑出StopIteration异常。

(4)释放对it的引用,即废弃迭代器对象。

(5)退出循环。


标准的迭代器接口有两个方法。

__next__

    返回下一个可用的元素,如果没有元素了,跑出StopIteration异常。

__iter__

    返回self, 以便在应该使用可迭代对象的地方使用迭代器,例如for循环中。


迭代器定义:

迭代器是这样的对象:实现了无参数的__next__ 方法,返回序列 中的下一个元素,

如果没有元素了,那么抛出StopIteration 异常。

Python 中的迭代器还实现了__iter__ 方法,因此迭代器也可以迭代。

因为内置的 iter(...) 函数会对序列做特殊处理,所以上面的Sentence类也可以迭代。


构建可迭代的对象和迭代器时经常会出现错误,原因是混淆了二者。要 知道,可迭代的对象有个__iter__ 方法,

每次都实例化一个新的迭代 器;而迭代器要实现__next__ 方法,返回单个元素,此外还要实现__iter__ 方法,返回迭代器本身。

因此,迭代器可以迭代,但是可迭代的对象不是迭代器。


可迭代的对象一定不能是自身的迭代器。也就是说,可迭代的对象 必须实现__iter__ 方法,但不能实现__next__ 方法。

另一方面,迭代器应该一直可以迭代。迭代器的__iter__ 方法应 该返回自身。


3,生成器

只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函 数。

调用生成器函数时,会返回一个生成器对象。也就是说,生成器函 数是生成器工厂。

用生成器实现最上面的Sentence类

再用惰性实现


4, 生成器表达式

生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表, 而是返回一个生成器,按需惰性生成元素。

也就是说,如果列表推导是 制造列表的工厂,那么生成器表达式就是制造生成器的工厂。

简而言之,相对于列表推导式,用()的就是生成器推导式

现在用生成器表达式实现Sentence类



itertools 中一些有用的生成器函数:


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容