生成器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))>>>gat 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,比如list
、dict
、str
都不是迭代器Iterator。把list
、dict
、str
等Iterable变成迭代器Iterator可以用内置函数iter()。
为什么list
、dict
、str
等Iterable内置数据类型不是迭代器Iterator?因为Python的迭代器Iterator表示的是一个数据流。Iterator对象可以被next函数调用,并不断返回下一个数据,并直到没有数据时候抛出StopIteration错误。可以把这个数据流看作有序序列,但是我们却不能提前知道序列的长度,只能通过next()函数计算出下一个数据,这种Iterator计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限长度的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结:
可以用for循环的都是可迭代对象Iterable;
generator
和list
、dict
、str
等对象都可以用for循环,即可迭代对象Iterable;
数据集合list
、dict
、str
等是循环的都是可迭代对象Iterable;,但不是迭代器Iterator。