前言
生成器是你在学习Python的过程中一定会遇到的一个东西,但是往往很少有文章能用一句话解释清楚到底什么是生成器,它可以干什么。本文的目标就是想通过简单的文字能够将生成器解释清楚,理解运行机理,懂的如何使用。那么久请看下面的介绍吧
生成器是什么
用简短的一句话解释什么是Python生成器,就是:使用了 yield 的函数就称为生成器(generator)
形如以下函数:
def fib(max):
a, b = 0, 1
while a < max:
yield a
a, b = b, a + b
其实,此函数是返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
生成器的作用
那么为什么要使用生成器呢?
主要是由于Python列表的数据都是存储在内存中的,如果数据量非常的时候,那么就非常的耗内存了。
于是有人就在想是不是我想要庞大的数据,但是确占用很少的内存,可不可以呢?于是有人想到如果列表元素按照某种规则(算法)推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必一次性创建完整的list,从而可以节省大量的空间。于是生成器就产生了。
所以生成器的作用就是:可以获得庞大的数据,同时占用内存小。
生成器函数的运行过程
当你了解生成器的作用后,你一定迫不及待的想知道生成器函数是如何运行的,对吧?别慌,接着看下面的讲解,看完你就知道生成器函数在调用的时候是如何运行的了。
一句话解释运行机理:
在next()调用生成器,生成器运行过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从yield的下一位置继续运行。
举例:
假如生成器是这个斐波拉契函数。
def fib(max):
a, b = 0, 1
while a < max:
yield a
a, b = b, a + b
通过next()执行调用
aa = fib(10)
# 使用方式一:next()
while True:
try:
print(next(aa), end=' ')
except StopIteration:
print('\n')
break
解释:当执行aa = fib(10)时,会创建一个生成器,如下所示接着执行到while语句中,while会一直执行,直到出现StopIteration才会通过break退出。
首先,我们来解释下try中的print(next(aa), end=’ ')语句,这句就是打印next(aa)的值,并且加一个空格作为间隔。
其次,我们来解释next(aa)这个语句,next(aa)表示执行一次生成器函数,当遇到yield的时候停止,并返回yield的值,再次执行next(aa)时,就会从上一次yield的位置继续往下执行,直到再次遇到yield时候停止,并返回yield的值。后面继续调用next方法,都重复前面的流程。
最后,当next(aa)继续执行时候,已经到了生成器最后位置时,便会抛出一个StopIteration的异常,从而终止生成器函数。
详细解释next(aa)每次调用时原函数的执行步骤
- 第一次调用next(aa)时, fib函数初始化 a, b = 0, 1,此时a = 0, b=1, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为0。进行下一次循环调用next(aa)
- 第二次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 1, b=1, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为1。进行下一次循环调用next(aa)
- 第三次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 1, b=2, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为1。进行下一次循环调用next(aa)
- 第四次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 2, b=3, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为2。进行下一次循环调用next(aa)
- 第五次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 3, b=5, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为3。进行下一次循环调用next(aa)
- 第六次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 5, b=8, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为5。进行下一次循环调用next(aa)
- 第七次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 8, b=13, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为8。进行下一次循环调用next(aa)
- 第八次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 13, b=21, 进入循环判定(a<10?),为假,于是结束循环,抛出异常,进入except StopIteration语句,打印换行符,再运行break,跳出while循环。
生成器的调用方式
正如前面介绍,生成器的调用方式就是next()。但是我们在平时使用时,也自己使用next()方法调用岂不是有点麻烦,于是Python也想到了这个问题,所以for循环,以及list等语句都自动使用next()方法(其实Python很多方法都自动支持使用next方式),并且巧妙处理了StopIteration异常,不会崩溃程序。
使用举例:还是上面的斐波拉契生成器除了显示的使用next(), 还可以有下面两种调用方式,list和for
# 使用方式二:list()
print('list(fib(100))=', list(fib(100)))
# 打印结果:
# list(fib(100))= [0, 1, 1, 2, 3, 5, 8]
# 使用方式三:for
for x in fib(100):
print(x, end=' ')
# 打印结果:
# 0 1 1 2 3 5 8
生成器的使用场景
节省内存
不确定数据大小
流式处理数据
无限的数据
好了,今天的生成器解释结束了,我相信你看完了,对生成器的了解应该是比较清晰了。如果你还有什么问题的话,可以给我留言,我们一起讨论。