python-38-降低内存开销的python迭代器

news2024/11/16 5:36:11

【进阶Python】第五讲:迭代器与生成器
python 迭代器和生成器
迭代是Python中常用且非常强大的一个功能,它可以用于访问集合、列表、字符串、字典等数据结构的元素。

我们经常使用循环和条件语句,我们也清楚哪些是可以迭代访问,但是具体它们之间有什么有什么异同之处?有哪些特点?什么是迭代器、什么是生成器、什么是可迭代对象?

在这里插入图片描述

1 可迭代对象

可迭代对象是Python中一个非常庞大的概念,它主要包括如下三类:

迭代器
序列
字典

从上图可以看出不同概念之间的关系,迭代器是可迭代对象的一个子集,而生成器又是迭代器的一个子集,是一种特殊的迭代器。除了迭代器之外,Python中还有序列、字典等可迭代对象。

现在已经直观的了解了可迭代对象与迭代器、生成器之间的关系,那么用Python语言怎么表述它们的区别呢?

可迭代对象需要实现__iter__方法。
迭代器不仅要实现__iter__方法,还需要实现__next__方法。

在使用层面,可迭代对象可以通过in和not in访问对象中的元素。

X = set([1,2,3,4,5])
print(X)
print(type(X))
print(1 in X)
print(2 not in X)
for x in X:
    print(x)

在这里插入图片描述
前面提到,可迭代对象实现了__iter__方法,但是它没有实现__next__,这也是判定迭代器和其他可迭代对象的关键之处,可以看一下通过next访问上述示例中可迭代对象X会报错。

next(X)
TypeError: 'set' object is not an iterator
它指明了set集合是一个可迭代对象,但不是迭代器。

2 迭代器

迭代器是可迭代对象的一个子集,它是一个可以记住遍历的位置的对象,它与列表、元组、集合、字符串这些可迭代对象的区别就在于next方法的实现,其他列表、元组、集合、字符串这些可迭代对象可以很简单的转化成迭代器。

迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有__next__方法的对象会前进到下一结果,而到一系列结果的末尾,则会引发StopIteration。任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开

2.1 为什么要用迭代器

使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销

比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:

from memory_profiler import profile

@profile
def test():
    fr = open("use.txt",encoding="utf-8").readlines()
    print(type(fr))
    for line in fr:
        print(line)

if __name__ == '__main__':
    test()

在这里插入图片描述

这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。

利用file的迭代器,我们可以这样写:

from memory_profiler import profile

@profile
def test():
    fr = open("use.txt",encoding="utf-8")
    print(type(fr))
    for line in fr:
        print(line)

if __name__ == '__main__':
    test()

在这里插入图片描述

这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。

2.2 内置的iter函数

通过Python内置的iter函数能够轻松把可迭代对象转化为迭代器。

X = [1,2,3]
print(type(X))
Y = iter(X)
print(type(Y),Y)
print(next(Y))
print(next(Y))
print(next(Y))
print(next(Y))

在这里插入图片描述
从上述示例中我们可以看出两点:
(1)通过iter函数把list转化成了迭代器。
(2)可迭代器能够记住遍历位置,能够通过next方法不断从前往后访问。

2.3 自定义迭代器

可以自己通过实现__iter__和__next__方法来定义迭代器。

class Iterator(object):
    def __init__(self, array):
        self.x = array
        self.index = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index < len(self.x):
            value = self.x[self.index]
            self.index += 1
        else:
            raise StopIteration
        return value
    
it = Iterator([1,2,3,4,5])
print(type(it))
for i in it:
    print(i)

3 生成器

从文章开头的流程图可以直观的看出,生成器是迭代器的子集,换句话说,生成器一定是迭代器,但是迭代器不全是生成器对象。

提及生成器就不得不提及一个Python中的关键字yiled,在Python中一个函数可以用yiled替代return返回值,这样的话这个函数就变成了一个生成器对象。

3.1 return返回方式

def generator(array):
    for i in array:
        return i

gen = generator([1, 2, 3])
print(type(gen),gen)
print(type(gen),gen)

输出
<class 'int'> 1
<class 'int'> 1

这是我们常见的return返回方式,这样的话generator函数获取的是一个int型对象。下面看一下换成yield关键字。

3.2 yield方式返回

def generator(array):
    for i in array:
        yield (i)

gen = generator([1, 2, 3])
print(type(gen),gen)
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))

在这里插入图片描述
这样的话获取的是一个生成器generator。

3.3 yield from方式返回

除了yield之外,在Python3.3之后还加入了yield from获取生成器,允许一个生成器将其部分操作委派给另一个生成器,使得生成器的用法变得更加简洁,yield from后面需要加上可迭代对象,这样可以把可迭代对象变成生成器,当然,这里的可迭代对象不仅包含列表、元组,还包含迭代器、生成器。yield from相对于yield的有几个主要优点:

(1)代码更加简洁。
(2)可以用于生成器嵌套。
(3)易于异常处理。

当我们需要访问多层/多维可迭代对象时,我们就不需要逐层的去用for … in …去访问,可以简单的通过yiled from把生成器委派给子生成器。

def generator(array):
    for sub_array in array:
        yield from sub_array

gen = generator([(1,2), (4,5)])

print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))

输出
1
2
4
5

生成器可以像迭代器那样使用iter和next方法。

读到这里可以会有疑惑,从这个示例看来生成器和迭代器并没有什么区别啊?为什么生成器还可以称得上是Python中的一大亮点?

首先它对比于迭代器在编码方面更加简洁,这是显而易见的,其次生成器运行速度更快,最后一点,也是需要着重说明的一点:节省内存。

也许在一些理论性实验、学术论文阶段可以不考虑这些工程化的问题,但是在公司做项目时,内存和资源占用是无法逃避的问题 。如果我们使用其他可迭代对象处理庞大的数据时,当创建或者返回值时会申请用于存储整个可迭代对象的内存,显然这是非常浪费的,因为有的元素当前我们用不到,也不会去访问,但它却一直占用这内存。这时候就体现了生成器的优点,它不是一次性把所有的结果都返回,而是当我们每读取一次,它会返回一个结果,当我们不读取时,它就是一个生成器表达式,几乎不占用内存。

3.4 生成器表达式

X = [1, 2, 3, 4, 5]
it = [i for i in X]
gen = (i for i in X)
print(type(X))
print(type(it))
print(type(gen))# 输出
<class 'list'>
<class 'list'>
<class 'generator'>

首先说一下it = [i for i in X],这种用法叫做列表生成式,在很多编程规范中非常推崇的一种替代for循环的方式,仔细看一下代码会发现,it = [i for i in X]与gen = (i for i in X)的区别非常小,只是一个用了中括号,一个用了小括号,但是它们的区别缺失非常大的,使用中括号的叫做列表生成式,获得的返回值是一个列表,而使用小括号叫做生成器表达式,获得的返回结果是一个生成器,这也是前面提到的,除了使用yield和yield from两个关键字外还可以使用生成器表达式获得生成器。

4 对比占用内存

4.1 list

from memory_profiler import profile

@profile
def test():
    gen = [i for i in range(100000)]
    print(type(gen))

if __name__ == '__main__':
    test()

在这里插入图片描述

4.2 iterator

from memory_profiler import profile

@profile
def test():
    gen = iter([i for i in range(100000)])
    print(type(gen))

if __name__ == '__main__':
    test()

在这里插入图片描述

4.3 generator

from memory_profiler import profile

@profile
def test():
    gen = (i for i in range(100000))
    print(type(gen))

if __name__ == '__main__':
    test()

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/176132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

结构型模式-代理模式

1.概述 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时&#xff0c;访问对象不适合或者不能直接引用目标对象&#xff0c;代理对象作为访问对象和目标对象之间的中介。 Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译…

Nacos 配置中心源码讲解

目录 1. 配置中心的优点 2. 配置模型结构 3. 配置中心 Server 端实现原理 3.1 新建配置 / 发布配置 3.2 查询配置 4. 配置中心 Client 端实现原理 4.1 发布配置 4.2 查询配置 4.3 监听机制 Listener 1. 配置中心的优点 运行时动态修改系统参数配置&#xff0c;不用重启…

排序算法解析:快排,归并 (全)

一、快排原始快排 算法思想&#xff1a;ps&#xff1a;排序的效果其实就是使一个数列中的每个数都满足左边数比它小、右边数比它大&#xff08;假设升序&#xff09;。接下来我们来了解快排&#xff1a;多次递归遍历&#xff0c;每单次遍历&#xff0c;设定一个限定值&#xff…

02 |「数据结构、逻辑结构、物理结构」基本概念简析

前言 前言&#xff1a;简析数据结构、逻辑结构、物理结构。 文章目录前言一、数据结构1. 简介2. 数据3. 结构4. 分析5. 分类1&#xff09;线性结构&#xff08;线性表&#xff09;2&#xff09;树结构3&#xff09;图结构二、逻辑结构与物理结构1. 为什么要有逻辑结构和物理结构…

SpringBoot+Vue--前端搭建-笔记1

前端搭建 首先安装node.js(百度) 官网下载地址&#xff1a;http://nodejs.cn/download 以前写的关于npm 后端了解的npm_biubiubiu0706的博客-CSDN博客 安装Node.js淘宝镜像加速器(cnpm) npm install cnpm -g(可以不安装) #建议使用如下语句解决npm速度慢的问题 好比设置仓…

代码随想录算法训练营三期 day 24 - 回溯 (1) (补)

回溯算法理论基础 什么是回溯法 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。回溯是递归的副产品&#xff0c;只要有递归就会有回溯。所以以下讲解中&#xff0c;回溯函数也就是递归函数&#xff0c;指的都是一个函数。 回溯法的效率 回溯的本质是穷举&…

【手把手教你学51单片机】中断的优先级

注&#xff1a;本文章转载自《手把手教你学习51单片机》&#xff01;因转载需要原文链接&#xff0c;故无法选择转载&#xff01; 如若侵权&#xff0c;请联系我进行删除&#xff01;上传至网络博客目的为了记录自己学习的过程的同时&#xff0c;同时能够帮助其他一同学习的小伙…

第四十三章 动态规划——最长单调序列模型

第四十三章 动态规划——最长单调序列模型一、最长单调序列模型1、模型母题2、思路分析&#xff08;两种方法&#xff1a;DP&#xff0c;贪心&#xff09;二、模型的应用1、AcWing 1017. 怪盗基德的滑翔翼&#xff08;1&#xff09;问题&#xff08;2&#xff09;分析&#xff…

C规范编辑笔记(十四)

往期文章&#xff1a; C规范编辑笔记(一) C规范编辑笔记(二) C规范编辑笔记(三) C规范编辑笔记(四) C规范编辑笔记(五) C规范编辑笔记(六) C规范编辑笔记(七) C规范编辑笔记(八) C规范编辑笔记(九) C规则编辑笔记(十) C规范编辑笔记(十一) C规范编辑笔记(十二) C规范编辑笔记(…

Linux进程学习【一】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f38a;每篇一句&#xff1a; 图片来源 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 Perseverance is not a long race; it is many short races one after another…

Linux基本功系列之rename命令实战

文章目录一. rename 命令介绍二. 语法格式及常用选项三. 参考案例3.1 将当前目录下所有.cfg的文件&#xff0c;替换为.txt结尾3.2 将所有出现mufeng的部分都替换为mufeng13.3 将mufeng0开头都变成mufeng00开头3.4 rename支持正则表示式总结前言&#x1f680;&#x1f680;&…

2023-1-22 刷题情况

积水面积 先祝大家新年快乐&#xff0c;新的一年&#xff0c;万事如意。 题目描述 一组正整数&#xff0c;分别表示由正方体叠起的柱子的高度。若某高度值为 xxx&#xff0c;表示由 xxx 个正立方的方块叠起&#xff08;如下图&#xff0c;0≤x≤50000 \le x \le 50000≤x≤5…

卷积神经网络进阶--基础知识

卷积神经网络进阶 b站课程链接碳基生物都能学会的神经网络&#xff08;跳着看的&#xff09; 因为我用的是pytorch&#xff0c;而该课程是用tenserflow的&#xff0c;所以主要记了一下理论 为什么要讲不同的网络结构 不同的网络结构解决的问题不同不同的网络结构使用的技巧不同…

【人工智能原理自学】卷积神经网络:打破图像识别的瓶颈

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文讲解卷积神经网络&#xff1a;打破图像识别的瓶颈&#xff0c;一起卷起来叭&#xff01; 目录一、手写体识别二、“炼丹”一、手写体识别 在机器学习、神经网络领域&#…

【数据分析】(task4)数据可视化

note matplotlib的四个容器&#xff1a; Figure&#xff1a;顶层级&#xff0c;用来容纳子 Axes&#xff0c;一组具体绘图元素和画布&#xff08;canvas&#xff09;。 画板。Axes&#xff1a;matplotlib宇宙的核心&#xff0c;容纳了大量元素用来构造一幅幅子图&#xff0c;一…

【QT5.9】与MFC对比学习笔记-感悟篇【2023.01.22】

简介 在公司从事MFC的程序维护一年两个月&#xff0c;期间因为公司被QT告侵权对QT产生了抵触的心情。现在无奈要用到&#xff0c;需要抓紧学习了。 正文 1.数据模型 先说下刚用到的模型&#xff0c;模型也叫数据模型&#xff0c;也就是耳熟的MVC架构中的M&#xff08;Model…

我用笨办法啃下了一个开源项目的源码!

目录 1、从最简单的源码开始&#xff1a;别幻想一步登天 2、循序渐进&#xff1a;先搞定底层依赖的技术 3、一定要以Hello World作为入口来阅读 4、抓大放小&#xff0c;边写注释边画图 5、反复三遍&#xff0c;真正理解源码 6、借力打力&#xff0c;参考源码分析书籍及博客 7…

研一寒假C++复习笔记--引用的使用

​​​​​​​ 目录 1--引用的基本语法 2--引用的注意事项 3--在函数参数中使用引用 4--引用作函数的返回值 5--引用的本质 6--常量引用 1--引用的基本语法 引用相当于给变量起别名&#xff0c;其基本语法如下&#xff1a; 数据类型 &别名 原名 # include <…

Linux操作系统之进程信号

代码存放在&#xff1a;https://github.com/sjmshsh/System-Call-Learn/tree/master/signal 我们先来看一张图&#xff0c;了解一下通过阅读本博客&#xff0c;你可以收获什么。 背景知识 首先我说明一点 信号 ! 信号量 我们这篇文章讲解的是信号&#xff0c;不是信号量 信…

POJ3263. Tallest Cow题解(c++ 前缀和)

POJ3263. Tallest Cow 传送门&#xff1a;Tallest Cow 题目&#xff1a; 有N头牛站成一行。两头作能够相支看见&#xff0c;当且仅当它们中间的牛身高都比它们矮。现在&#xff0c;我们只知道其中最高的牛是第P头&#xff0c;它的身高是H&#xff0c;不知道剩余N-1头牛的身高。…