当前位置: 首页 > news >正文

深入理解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 

生成器的使用场景

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

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

相关文章:

  • SpringBoot+Vue项目校园商铺系统
  • “不学数学就去当厨子”,兰大校友入选全球竞赛最强10人,决赛最后几小时才想起做题...
  • Python基础_判断语句(if、elif、else)、if 嵌套、逻辑运算符(and、or、not )、随机数的处理
  • 【C语言】小游戏系列——扫雷(内含详细过程)
  • C++系列文章 —— 类和对象篇(上)(从入门到精通合集)
  • 7.5 文件系统
  • java计算机毕业设计伊伊物流公司的管理系统源码+数据库+系统+lw文档+部署
  • PCB设计笔记
  • 图卷积神经网络(GCN)
  • 【数据结构】八大排序
  • D*(Dynamic A*)路径规划算法
  • 16.12 - 基于数据流设计用例
  • 大数据工程师、数据挖掘师和数据分析师有啥区别
  • 面试让我手写红黑树?!
  • C/C++学习笔记 资源获取是初始化 (RAII) 理解
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • CSS实用技巧
  • css选择器
  • Electron入门介绍
  • iOS 系统授权开发
  • Java深入 - 深入理解Java集合
  • java中的hashCode
  • JS+CSS实现数字滚动
  • k个最大的数及变种小结
  • Promise面试题,控制异步流程
  • Python 基础起步 (十) 什么叫函数?
  • Redux 中间件分析
  • ucore操作系统实验笔记 - 重新理解中断
  • 前端面试之CSS3新特性
  • 使用putty远程连接linux
  • 移动端解决方案学习记录
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 自定义函数
  • 最近的计划
  • ​第20课 在Android Native开发中加入新的C++类
  • #{}和${}的区别?
  • #pragam once 和 #ifndef 预编译头
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (MATLAB)第五章-矩阵运算
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (pojstep1.3.1)1017(构造法模拟)
  • (二)Linux——Linux常用指令
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (六)Hibernate的二级缓存
  • (七)Java对象在Hibernate持久化层的状态
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • .apk文件,IIS不支持下载解决
  • .gitattributes 文件
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .NET Core跨平台微服务学习资源