迭代器(iterator)与生成器(generator)是 Python 中比较常用又很容易混淆的两个概念,今天就把它们梳理一遍,并举一些常用的例子。
for 语句与可迭代对象(iterable object):
这些可以用在 for 语句进行循环的对象就是可迭代对象。除了内置的数据类型(列表、元组、字符串、字典等)可以通过 for 语句进行迭代,我们也可以自己创建一个容器,包含一系列元素,可以通过 for 语句依次循环取出每一个元素,这种容器就是迭代器(iterator)。除了用 for 遍历,迭代器还可以通过 next() 方法逐一读取下一个元素。要创建一个迭代器有3种方法,其中前两种分别是:
为容器对象添加 iter() 和 next() 方法;iter() 返回迭代器对象本身 self,next() 则返回每次调用 next() 或迭代时的元素;
内置函数 iter() 将可迭代对象转化为迭代器
上面的next方法调用错误。在Python3.x中若使用generator的next属性,应该这么调用:
创建迭代器对象的好处是当序列长度很大时,可以减少内存消耗,因为每次只需要记录一个值即刻(经常看到人们介绍 Python 2.7 的 range 函数时,建议当长度太大时用 xrange 更快,在 Python 3.5 中已经去除了 xrange 只有一个类似迭代器一样的 range)。
生成器
前面说到创建迭代器有3种方法,其中第三种就是生成器(generator)。生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议。
也就是说,yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。
生成器通过 yield 语句快速生成迭代器,省略了复杂的iter() &next() 方式。
简单来说,yield 语句可以让普通函数变成一个生成器,并且相应的next()方法返回的是yield后面的值。一种更直观的解释是:程序执行到yield会返回值并暂停,再次调用next()时会从上次暂停的地方继续开始执行:
生成器表达式
在开始介绍生成器表达式之前,先看看我们比较熟悉的列表解析( List comprehensions),列表解析一般都是下面的形式。
[expr for iter_var in iterable if cond_expr]
迭代iterable里所有内容,每一次迭代后,把iterable里满足cond_expr条件的内容放到iter_var中,再在表达式expr中应该iter_var的内容,最后用表达式的计算值生成一个列表。
例如,生成一个list来保护50以内的所以奇数:
[i for i in range(50) if i%2]
生成器表达式当序列过长, 而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析。生成器表达式的语法和列表解析一样,只不过生成器表达式是被()括起来的,而不是[],如下:
(expr for iter_var in iterable if cond_expr)
看一个例子:
gen = (i for i in range(50) if i%2) print(gen) print([i for i in gen])
#执行结果
at 0x7f7fac1f3f30> [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49]
生成器表达式并不是创建一个列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目”产生”(yield)出来。 生成器表达式使用了”惰性计算”(lazy evaluation),只有在检索时才被赋值(evaluated),所以在列表比较长的情况下使用内存上更有效。
继续看一个例子:
gen = (i for i in range(50) if i%2) print("__iter__" in dir(gen)) print("__next__" in dir(gen)) print(sum(gen)) print([i for i in gen])
#执行结果
True
True
625
[]
从这个例子中可以看到,生成器表达式产生的生成器,它自身是一个可迭代对象,同时也是迭代器本身。
总结
本文介绍了Python迭代器和生成器的相关内容。
通过实现迭代器协议对应的iter()和next()方法,可以自定义迭代器类型。对于可迭代对象,for语句可以通过iter()方法获取迭代器,并且通过next()方法获得容器的下一个元素。
像列表这种序列类型的对象,可迭代对象和迭代器对象是相互独立存在的,在迭代的过程中各个迭代器相互独立;但是,有的可迭代对象本身又是迭代器对象,那么迭代器就没法独立使用。
生成器是一种特殊的迭代器,内部支持了生成器协议,不需要明确定义iter()和next()方法。
生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果。
Python学习交流聚集地请看评论区
领取专属 10元无门槛券
私享最新 技术干货