python中大数据文件读取
python中经常会遇到读取大文件的场景。文件较小时,我通常采用下面方法,readlines(),该方法会一次性读取文件的所有行,并将其放入list中,最后存入内存中。可想而知,当文件较大是,内存占用会非常高,甚至造成内存溢出,进程被系统kill掉。
# 读取方式一
with open(file_path, 'r+', encoding='utf-8') as f:
count = 0
for line in f.readlines():
count += 1
print(count)
运行前:
系统总计内存:8056.3M
系统已经使用内存:6264.3M
系统空闲内存:1791.3M
20465
运行后:
系统总计内存:8056.3M
系统已经使用内存:6266.3M
系统空闲内存:1789.3M
实验采用的文件大小为800k,不算太大,可以看出,占用内存2M。
推荐方法:迭代器迭代遍历
python中有个迭代器的概念。迭代,其实就是对序列中的元素进行处理的一种方式。
下面我们用可迭代对象进行迭代遍历,测试其占用内存大小。for line in f,会自动使用缓存IO以及内存管理。
# 读取方式二
with open(file_path, 'r+', encoding='utf-8') as f:
count = 0
for line in f:
count += 1
print(count)
运行前:
系统总计内存:8056.3M
系统已经使用内存:5883.3M
系统空闲内存:2172.3M
20465
运行后:
系统总计内存:8056.3M
系统已经使用内存:5884.3M
系统空闲内存:2171.3M
实验采用的文件大小为800k,不算太大,可以看出,占用内存1M,比第一种方法节约内存。
推荐方法:生成器
生成器(Generator)是创建迭代器的简单而强大的工具。写起来就像是正规的函数,只是在返回数据的时候需要使用yield语句。
# 读取方式三
# 生成器函数,用于自定义创建迭代器read_fileline_generator
def read_fileline_generator(file, size=4096):
while 1:
data = file.read(size)
if not data:
break
yield data
with open(file_path, 'r+', encoding='utf-8') as f:
count = 0
for line in read_fileline_generator(f):
count += 1
运行前:
系统总计内存:8056.3M
系统已经使用内存:5719.3M
系统空闲内存:2336.3M
运行后:
系统总计内存:8056.3M
系统已经使用内存:5720.3M
系统空闲内存:2335.3M
实验采用的文件大小为800k,不算太大,可以看出,占用内存1M,比第一种方法节约内存。
生成器
上面我们采用生成器创建了一个迭代器对文件进行迭代遍历。那什么是生成器呢?我们来仔细研究下生成器,生成器类似于返回值为数组的一个函数。
为了支持迭代协议,拥有yield语句的函数被编译为生成器,这类函数被调用时返回一个生成器对象,返回的对象支持迭代接口,即成员方法__next()__继续从中断处执行执行。
但不同于一般的函数:
一般函数一次性返回所有数值的数组,而生成器函数一次只产生数组中的一个值,这样消耗的内存会大大减少;
一般函数执行完毕后返回值并退出,而生成器函数利用yield关键字,给调用者返回一个值,保留当前执行状态,并自动挂起,便于后面next调用时继续执行。
生成器使用next()函数会比较繁琐,而for循环可以为生成器自动触发next函数。
python中生成器有两种方式:
- **生成器函数:**也是用def来定义,利用关键字yield一次返回一个结果,阻塞,重新开始
- **生成器表达式:**返回一个对象,这个对象只有在需要的时候才产生结果
上面我们应用了生成器函数,那么生成器表达式是什么呢?
生成器表达式和列表解析类似,但它使用圆括号,而列表解析式用中括号。
# 列表解析式
list = [ x**2 for x in range(5)]
print(list)
[0, 1, 4, 9, 16]
# 生成器表达式
generator_list = (x**2 for x in range(5))
print(generator_list)
<generator object <genexpr> at 0x0EE83840>
print(next(generator_list))
0
print(next(generator_list))
1
print(next(generator_list))
4
print(next(generator_list))
9
print(next(generator_list))
16
print(next(generator_list))
Traceback (most recent call last):
File "<input>", line 1, in <module>
StopIteration
测试代码
# python 读取较大数据文件
import psutil
mem = psutil.virtual_memory()
print('运行前:')
print('系统总计内存:%d.3M' % (float(mem.total)/1024/1024))
print('系统已经使用内存:%d.3M' % (float(mem.used)/1024/1024))
print('系统空闲内存:%d.3M' % (float(mem.free)/1024/1024))
file_path = 'H:\\study\\python\\long.txt'
# 读取方式一
# with open(file_path, 'r+', encoding='utf-8') as f:
# count = 0
# for line in f.readlines():
# count += 1
# print(count)
# 读取方式二
# with open(file_path, 'r+', encoding='utf-8') as f:
# count = 0
# for line in f:
# count += 1
# print(count)
# 读取方式三
def read_fileline_generator(file, size=4096):
while 1:
data = file.read(size)
if not data:
break
yield data
with open(file_path, 'r+', encoding='utf-8') as f:
count = 0
for line in read_fileline_generator(f):
count += 1
mem2 = psutil.virtual_memory()
print('运行后:')
print('系统总计内存:%d.3M' % (float(mem2.total) / 1024 / 1024))
print('系统已经使用内存:%d.3M' % (float(mem2.used) / 1024 / 1024))
print('系统空闲内存:%d.3M' % (float(mem2.free) / 1024 / 1024))