Python进阶08 生成器

一、生成器

通过列表推导,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

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

二、生成器方法一

第一种方法很简单,只要把一个列表推导的[]改成(),就创建了一个generator:

>>> L = [x * x for x in range(5)]
>>> L
[0, 1, 4, 9, 16]
>>> g = (x * x for x in range(5))
>>> g
<generator object <genexpr> at 0x1044299a8>

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以通过next()函数获得generator的下一个返回值,从而获得generator的每一个元素。

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

 

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
当然,遍历generator正确的方法是使用for循环,因为generator也是可迭代对象:

>>> g = (x * x for x in range(5))
>>> for x in g:
...     print(x)
...
0
1
4
9
16

我们创建了一个generator后,基本上不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

三、生成器方法二

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
>>> f = fib(6)
>>> f
<generator object fib at 0x1044299a8>

 

这里,比较难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5
>>> next(f)
8
>>> next(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: done

 

实际上,把函数改成generator后,我们也基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

>>> for n in fib(6):
...     print(n)
...
1
1
2
3
5
8

四、总结

generator是非常强大的工具,在Python中,可以简单地把列表推导改成generator,也可以通过函数实现复杂逻辑的generator。
对于列表推导改成的generator来说,它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。
对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环随之结束。

请注意区分普通函数和generator函数,普通函数调用直接返回结果:

>>> r = abs(6)
>>> r
6

generator函数的“调用”实际返回一个generator对象:

>>> f = fib(6)
>>> f
<generator object fib at 0x104429a20>

 

参考文章(这篇文章比书上讲的更通俗易懂):

https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128

posted on 2020-03-25 15:33  麦克煎蛋  阅读(187)  评论(0编辑  收藏  举报