首页 > 编程 > Python > 正文

详解Python的迭代器、生成器以及相关的itertools包

2020-02-23 00:30:18
字体:
来源:转载
供稿:网友

对数学家来说,Python这门语言有着很多吸引他们的地方。举几个例子:对于tuple、lists以及sets等容器的支持,使用与传统数学类似的符号标记方式,还有列表推导式这样与数学中集合推导式和集的结构式(set-builder notation)很相似的语法结构。

另外一些很吸引数学爱好者的特性是Python中的iterator(迭代器)、generator(生成器)以及相关的itertools包。这些工具帮助人们能够很轻松的写出处理诸如无穷序列(infinite sequence)、随机过程(stochastic processes)、递推关系(recurrence relations)以及组合结构(combinatorial structures)等数学对象的优雅代码。本文将涵盖我关于迭代器和生成器的一些笔记,并且有一些我在学习过程中积累的相关经验。
Iterators

迭代器(Iterator)是一个可以对集合进行迭代访问的对象。通过这种方式不需要将集合全部载入内存中,也正因如此,这种集合元素几乎可以是无限的。你可以在Python官方文档的“迭代器类型(Iterator Type)”部分找到相关文档。

让我们对定义的描述再准确些,如果一个对象定义了__iter__方法,并且此方法需要返回一个迭代器,那么这个对象就是可迭代的(iterable)。而迭代器是指实现了__iter__以及next(在Python 3中为__next__)两个方法的对象,前者返回一个迭代器对象,而后者返回迭代过程的下一个集合元素。据我所知,迭代器总是在__iter__方法中简单的返回自己(self),因为它们正是自己的迭代器。

一般来说,你应该避免直接调用__iter__以及next方法。而应该使用for或是列表推导式(list comprehension),这样的话Python能够自动为你调用这两个方法。如果你需要手动调用它们,请使用Python的内建函数iter以及next,并且把目标迭代器对象或是集合对象当做参数传递给它们。举个例子,如果c是一个可迭代对象,那么你可以使用iter(c)来访问,而不是c.__iter__(),类似的,如果a是一个迭代器对象,那么请使用next(a)而不是a.next()来访问下一个元素。与之相类似的还有len的用法。

说到len,值得注意的是对迭代器而言没必要去纠结length的定义。所以它们通常不会去实现__len__方法。如果你需要计算容器的长度,那么必须得手动计算,或者使用sum。本文末,在itertools模块之后会给出一个例子。

有一些可迭代对象并不是迭代器,而是使用其他对象作为迭代器。举个例子,list对象是一个可迭代对象,但并不是一个迭代器(它实现了__iter__但并未实现next)。通过下面的例子你可以看到list是如何使用迭代器listiterator的。同时值得注意的是list很好地定义了length属性,而listiterator却没有。

>>> a = [1, 2]>>> type(a)<type 'list'>>>> type(iter(a))<type 'listiterator'>>>> it = iter(a)>>> next(it)1>>> next(it)2>>> next(it)Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration>>> len(a)2>>> len(it)Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: object of type 'listiterator' has no len()            
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表