通过列表生成式,我们可以直接创建一个列表,但是收到内存限制,列表容量是有限的。而且创建一个包含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 <generator object <genexpr> at 0x104feab40>
我们可以通过下标打印出,list的每个元素,那么怎么打印生成器对应的元素呢?
如果要一个一个打印,可以使用next()方法。next(g)
不过我们一般使用for循环:
生成器的两种形式(Python有两种不同的方式提供生成器)
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。生成器和函数的执行流程不一样,函数是顺序执行,遇到return语句或者最后一行函数语句就结束。在调用生成器运行过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield值。并在下一次执行next方法时,从当前位置继续运行。
yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator。
下面为一个可以无穷生产奇数的生成器函数。(可以看出使用生成器是十分方便的)
def odd(): n=1 while True: yield n n+=2 odd_num = odd() count = 0 for o in odd_num: if count >=5: break print(o) count +=1
2、生成器表达式:类似于列表推导,但,生成器返回按需生产结果的一个对象,而不是一次构建一个结果列表。
g = (i for i in range(10**100))#生成器表达式 L = [i for i in range(10**100)]#列表生成式
一个小例子
斐波那契数列的实现,虽然它无法使用列表推导式打印出来,但是用函数可以轻易的打印出来
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print b 5 a, b = b, a + b 6 n = n + 1
怎么能用生成器的方法来实现呢?只需把return改成yield就好。
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1
In [67]: fib(6) Out[67]: <generator object fib at 0x000000000484BD58> In [68]: a = fib(6) In [69]: next(a) Out[69]: 1 In [70]: next(a) Out[70]: 1 In [71]:
当我们调用fib(6)的时候,提示这是一个generator对象。是不能这么打印的。