深入理解Python生成器

前言

生成器是你在学习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)每次调用时原函数的执行步骤

  1. 第一次调用next(aa)时, fib函数初始化 a, b = 0, 1,此时a = 0, b=1, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为0。进行下一次循环调用next(aa)
  2. 第二次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 1, b=1, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为1。进行下一次循环调用next(aa)
  3. 第三次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 1, b=2, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为1。进行下一次循环调用next(aa)
  4. 第四次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 2, b=3, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为2。进行下一次循环调用next(aa)
  5. 第五次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 3, b=5, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为3。进行下一次循环调用next(aa)
  6. 第六次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 5, b=8, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为5。进行下一次循环调用next(aa)
  7. 第七次调用next(aa)时,fib函数从yield a的下一条语句开始执行,执行a, b = b, a + b, 此时a = 8, b=13, 进入循环判定(a<10?),为真,通过yield a,返回此时打印为8。进行下一次循环调用next(aa)
  8. 第八次调用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 

生成器的使用场景

节省内存
不确定数据大小
流式处理数据
无限的数据

好了,今天的生成器解释结束了,我相信你看完了,对生成器的了解应该是比较清晰了。如果你还有什么问题的话,可以给我留言,我们一起讨论。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木瓜~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值