生成器表达式
上一节我们讲到了列表生成式、字典生成式、集合生成式、那有没有元组生成式呢?我们用其他生成式的方式将其包在()中构造一个试试看。
t=(i*i for i in range(1,6))
print(t)
<generator object <genexpr> at 0x00000000020D7E58>
我们得到是一个叫做生成器(generator)的对象。因为元组是不可变类型的对象,无法在代码中动态地创建元组对象。
那么如何生成器的元素呢?方法有两种
1.调用内置函数next,直到抛出StopIteration时表示没有更多的元素了。
2.使用for-in循环遍历,从而不需要关心StopIteration的错误。
>>>t=(i*i for i in range(1,6))
>>>print(next(t))
1
>>>print(next(t))
4
>>>print(next(t))
9
>>>print(next(t))
16
>>>print(next(t))
25
>>>print(next(t))
StopIteration
>>>t=(i*i for i in range(1,6))
>>>for item in t:
... print(item)
1
4
9
16
25
生成器是惰性推算的,其中保存的是推算所有的算法。当使用next()或for-in循环时,才推算出需要的元素。受到内存限制,如列表生成式的容量肯定是有限的。当元素较多时,使用生成器能大量地节省内存。
生成器函数
除了上述的生成器表达式外,算法比较复杂时还可以使用生成器函数。生成器函数使用关键字yield返回推算的元素。
让我们来看一个斐波那契数列的例子
def fib(n):
i=0
a,b=1,1
while i<n:
print(a)
a,b=b,a+b
i+=1
fib(6)
运行以上代码得到的输出如下,我们得到了斐波那契数列的前6个元素。
1
1
2
3
5
8
有没有方法能保留所有的斐波那契数列,需要时传入所需个数的参数就能推算出指定的元素。
只需对上述的代码稍作改动即可。
def fib(n):
i=0
a,b=1,1
while i<n:
yield a #将此处改为yield
a,b=b,a+b
i+=1
fib(6)
此时调用fib(6)不会返回任何值,generator函数的“调用”实际返回一个generator对象。
我们同样可以用next()或for-in循环来获取元素。
>>>print(fib(6))
>>>ge=fib(6)
>>>print(next(ge))
>>>print(next(ge))
>>>print(next(ge))
>>>print(next(ge))
>>>print(next(ge))
>>>print(next(ge))
<generator object fib at 0x0000000001E47F48>
1
1
2
3
5
8
如果此时我们继续上面的代码对用for-in循环将得不到任何输出。
for item in ge:
print(item)
因为生成器只支持一次迭代。因此需要重新获取生成器。
ge=fib(6)
for item in ge:
print(item)
1
1
2
3
5
8
generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。