1、迭代器与生成器
此部分主要参考:如何更好地理解Python迭代器和生成器?(知乎)
迭代器协议
:对象需要提供next方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。
迭代器就是用于迭代操作的对象,它像列表一样可以迭代获取其中的每一个。它与我们平时使用for循环遍历列表中所有元素的区别在于,构建迭代器的时候,不像列表把所有元素一次性加载到内存,而是以一种延迟计算方式返回元素,这就是我们前面所说,为什么可以极大的减小内存的占用。它是等到调用next
方法时候才返回该元素(本质上 for 循环就是不断地调用迭代器的next
方法)。
生成器则是提供了一种延迟操作,其不用return返回结果,而是使用yield
。生成器函数就是常规函数的定义,但是,使用yield
语句而不是return语句返回结果。yield
语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行。
在我们常用的将自己的数据文件读入函数中,其实用的就是迭代器。因为在Python中,文件对象实现了迭代器协议,for循环直接使用迭代器协议访问对象即可。
生成器协议其实在我们进行深度学习的训练中,使用batch
函数对原本总体进行抽样时就会用到,因为我们在每个epoch
中采用的是不放回抽样,所以恰好可以使用生成器协议(这只是一个特例,其实应用的也非常广泛)。
2、pandas库中的迭代器
使用Python对数据进行读入,通常我们使用的简单方法是使用pandas库中read系列函数(read_csv
、read_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
,查看剩下的所有内容。