Python中的生成器与迭代器

本文介绍了Python中的迭代器和生成器概念,包括它们的工作原理、如何使用以及在实际编程中的应用案例。特别关注了在大数据处理场景下,通过迭代器和生成器减少内存占用的有效方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、迭代器与生成器

此部分主要参考:如何更好地理解Python迭代器和生成器?(知乎)

迭代器协议:对象需要提供next方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。

迭代器就是用于迭代操作的对象,它像列表一样可以迭代获取其中的每一个。它与我们平时使用for循环遍历列表中所有元素的区别在于,构建迭代器的时候,不像列表把所有元素一次性加载到内存,而是以一种延迟计算方式返回元素,这就是我们前面所说,为什么可以极大的减小内存的占用。它是等到调用next方法时候才返回该元素(本质上 for 循环就是不断地调用迭代器的next方法)。

生成器则是提供了一种延迟操作,其不用return返回结果,而是使用yield。生成器函数就是常规函数的定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行。

在我们常用的将自己的数据文件读入函数中,其实用的就是迭代器。因为在Python中,文件对象实现了迭代器协议,for循环直接使用迭代器协议访问对象即可。

生成器协议其实在我们进行深度学习的训练中,使用batch函数对原本总体进行抽样时就会用到,因为我们在每个epoch中采用的是不放回抽样,所以恰好可以使用生成器协议(这只是一个特例,其实应用的也非常广泛)。


2、pandas库中的迭代器

使用Python对数据进行读入,通常我们使用的简单方法是使用pandas库中read系列函数(read_csvread_excel等),就能对通常情况下绝大多数的数据进行读入。使用这些函数时,我们就可使用迭代器的方法,将一个非常大的文件,分块分批的读入,这样做可以极大的减小了内存的占用。首先来了解一下其中的一些参数。

添加chunk_size参数,可以将数据划分成多少份进行读入,例如我们要将数据划分成5份进行读入:

chunk_size = 5
reader = pd.read_csv('文件名', chunksize = chunk_size)

这样我们就产生了一个迭代器:reader,然后我们再使用for循环进行读入:

for i, data in enumerate(reader)

其中data就是我们划分之后的每个小data了。

这个操作在做数据比赛时非常有用,如果数据量过大,电脑内存不够,我们可以不用将文件拆成小文件进行读入,而是可以直接使用迭代器的方法,对一个大文件分块读入,这样非常方便地对数据进行处理。同时,这样做也可以大大提升读取的速度,减少内存的占用。

或者直接设置参数:iterator=True,这样我们就可以使用

reader.get_chunk(5)

只快速读入前五行,先看看整个数据各个维度的大致情况,这是一个非常实用便捷的功能。但如果我们要指定哪些行进行读入,使用read_csv就没办法进行操作了。


3、Python中自带的迭代器与生成器

这时就需要使用传统的Python中自带的迭代器读入方法(假设从1000行开始读):

f = open('文件名', 'r')
for line in f.readlines()[999:len(f.readlines())-1]

同时对于非常非常大的数据进行处理,通常情况下也是使用自带的原生函数进行逐行读入,其速度也非常快。

这类型迭代器的基本想法就是:我们没有将全部的文本文件装入内存,而是每次只指向迭代器的下一个指针位置,对这个指针位置进行某种操作(上面的代码中进行了split()操作),操作完后,内存的指针不变,等待下一次操作。
而如果要读入指定行,还可以使用linecache库中的getline函数,类似如下的操作。

import linecache
the_line = linecache.getline('文件名', 222)

如果我们只需要一个简单地访问数据文件的迭代器,单纯地输出文件的每行信息,就可以使用如下代码:

with open('D:/Desktop/myfile.data') as f:
        for line in f:
            print(f)

一个最简单的生成器协议其实就是将我们原本的快速for循环生成list方法中的中括号修改成小括号,就可以产生一个生成器了。我们想一个一个生成0-4之间所有整数的平方,使用生成器协议的代码如下:

In [1]: y = (x**3 for x in range(6))
In [2]: y
Out[2]: <generator object <genexpr> at 0x0000023F0FF5D938>

In [3]: next(y)
Out[3]: 0

In [4]: next(y)
Out[4]: 1

In [5]: next(y)
Out[5]: 8

In [6]: list(y)
Out[6]: [27, 64, 125]

简单来说,就是使用next函数,一个一个调用,调用了一个就扔掉一个,我们也能直接使用list,查看剩下的所有内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值