生成器generator

通过列表生成式可以直接创建列表,但是受到内存限制,列表容量肯定是有限的。而且创建一个包含100万个元素的列表,不仅占用大量内存,如果我们仅仅需要访问其中某几个元素,那么绝大多数空间是浪费了。

所以,如果列表是按照某种算法推算出来的,那么我们可以在循环过程中推算出后面的所有元素。这样就不必创建完整的 list了,从而节省了大量的存储空间。在Python中,一边循环一边计算的机制,成为生成器generator。

要创建一个generator有很多种方法。第一种方法很简单,就是把列表生成式[]改成(),就创建了一个generator:

>>>L = [x*x for x in range(10)]>>>L[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>>g = (x*x for x in range(10))>>>g
 at 0x10e357a00>

那要怎么全部打印出来呢?用内置函数next()

>>> next(g)0>>> next(g)1>>> next(g)4>>> next(g)9>>> next(g)16>>> next(g)25>>> next(g)36>>> next(g)49>>> next(g)64>>> next(g)81>>> next(g)Traceback (most recent call last):File "
", line 1, in 
StopIteration

generator保存的是算法,每次调用next(g),就计算出下一个值,知道计算到最后一个元素,没有更多元素时,抛出StopIteration的错误。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

>>> g = (x * x for x in range(10))>>> for n in g:...     print(n)...

斐波拉切数列用列表生成式写不出来,但是用代码写出来很容易:

def fib(max):    n, a, b = 0, 0, 1    while n < max:        print(b)        a, b = b, a+b        n = n+1    return 'Done'

注意赋值语句:

a, b = b, a+b

相当于:

t = (b, a + b) # t是一个tuplea = t[0]b = t[1]

但是不必显式的声明临时变量t。

从上面可以看出,列表是按照斐波拉切算法推导出来的,这和生成器generator很相似。

只需要把上面代码print(b)改成yield b.

函数中有关键字yield就和普通的函数不一样了。普通函数流程是顺序执行到结束返回。而含有yield的函数,每次调用next()的时候执行,遇到yield返回,再次执行的时候从上次yield语句处继续执行。

def fib(max):    n, a, b = 0, 0, 1    while n < max:        yield b        a, b = b, a+b        n = n+1        print(f)#运行结果为
f = fib(10)for n in f:    print n

例子:杨辉三角

迭代器iterator

我们知道,可以用for()循环的有以下几种:

第一种是集合数据类型如list,tuple,dic,set,str等;

第二种是生成器generator,包括带有yield的函数。

这些能使用for循环的对象统称为可迭代对象Iterable

可以使用isinstance()来判断一个对象是否是可迭代对象。

>>>from collections import Iterable>>>isinstance([], Iterable)True>>>isinstance((x*x for x in range(10)), Iterable)True>>>isinstance(100, Iterable)False

而生成器不但可以用于for循环,还可以被next()函数调用并且返回下一个值,直到最后抛出StopIteration错误,表示无法继续返回下一个值了。

可以被next()调用,并且可以不断返回下一个值的对象,被成为迭代器Iterator

可以使用isinstance()判断对象是否是迭代器Iterator

>>>from collections import Iterator>>>isinstance((x*x for x in range(10)), Iterator)True>>>isinstance([], Iterator)False>>> isinstance('abc', Iterator)False>>> isinstance(iter([]), Iterator)True

生成器都是迭代器Iterator,但是可迭代对象Iterable不一定都是生成器Iterator,比如listdictstr都不是迭代器Iterator。把listdictstrIterable变成迭代器Iterator可以用内置函数iter()

为什么listdictstrIterable内置数据类型不是迭代器Iterator?因为Python的迭代器Iterator表示的是一个数据流。Iterator对象可以被next函数调用,并不断返回下一个数据,并直到没有数据时候抛出StopIteration错误。可以把这个数据流看作有序序列,但是我们却不能提前知道序列的长度,只能通过next()函数计算出下一个数据,这种Iterator计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限长度的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

小结:

可以用for循环的都是可迭代对象Iterable

generatorlistdictstr等对象都可以用for循环,即可迭代对象Iterable

数据集合listdictstr等是循环的都是可迭代对象Iterable;,但不是迭代器Iterator