python读写文件的都很简单,直接一个with open就解决了,但是这也只是在学习基础知识时候的小案例,不能轻易放入项目中。

1.read()与readlines()


6with open(file_path, 'rb') as f:
sha1Obj.update(f.read())
or
with open(file_path, 'rb') as f:
for line in f.readlines():
print(line)

这对方法在读取小文件时确实不会产生什么异常,但是一旦读取大文件,很容易会产生MemoryError,也就是内存溢出的问题。

####Why Memory Error?

我们首先来看看这几个方法:

read方法

read([size])方法从文件当前位置起读取size个字节,若无参数size(size=-1),则表示读取至文件结束EOF为止,当文件大小大于可用内存时,自然会发生内存溢出的错误。

readline方法

该方法每次读出一行内容,所以,读取时占用内存小,比较适合大文件,该方法返回一个字符串对象。

可选参数 size 的含义同上。它是以行为单位返回字符串,也就是每次读一行,依次循环,如果不限定 size,直到最后一个返回的是空字符串,意味着到文件末尾了(EOF).

readlines方法

size参数 同上。它返回的是以行为单位的列表,即相当于先执行 readline() ,得到每一行,然后把这一行的字符串作为列表中的元素塞到一个列表中,最后将此列表返回。

readlines构造的是list|而不是iter,所以所有的内容都会保存在内存之上,文件内容过大同样也会发生内存溢出的错误。

2.正确的用法

在实际运行的系统之中如果写出上述代码是十分危险的,。所以接下来我们来了解一下正确用法。

如果是二进制文件推荐用如下这种写法,可以自己指定缓冲区有多少byte。显然缓冲区越大,读取速度越快。


8with open(file_path, 'rb') as f:
while True:
buf = f.read(1024)
if buf:
Obj.update(buf)
else:
break

而如果是文本文件,则可以用readline方法或直接迭代文件(python这里封装了一个语法糖,二者的内生逻辑一致,不过显然迭代文件的写法更pythonic )每次读取一行,效率是比较低的。笔者简单测试了一下,在3G文件之下,大概性能和前者差了20%.


11with open(file_path, 'rb') as f:
while True:
line = f.readline()
if buf:
print(line)
else:
break
with open(file_path, 'rb') as f:
for line in f:
print(line)

所以在使用python读取文件的时候如果是大文件(接近内存或大于1G)建议使用read(size=1024)(二进制文件)或者readline()(文本文件)。如果文件不大,而且需要经常使用文件数据,则可以直接使用read()和readlines()把数据全加载到内存里。

3.内存检测工具的介绍

对于python代码的内存占用问题,对于代码进行内存监控十分必要。这里推荐两个小工具来检测python代码的内存占用。

####memory_profiler

首先先用pip安装memory_profiler1pip install memory_profiler

memory_profiler是利用python的装饰器工作的,所以我们需要在进行测试的函数上添加装饰器。


15from hashlib import sha1
import sys
@profile
def my_func():
sha1Obj = sha1()
with open(sys.argv[1], 'rb') as f:
while True:
buf = f.read(10 * 1024 * 1024)
if buf:
sha1Obj.update(buf)
else:
break
print(sha1Obj.hexdigest())
if __name__ == '__main__':
my_func()

之后在运行代码时加上”-m memory_profiler“

就可以了解函数每一步代码的内存占用了1pyhton -m memory_profiler test.py

guppy

依仍然是通过pip先安装guppy1pip install guppy

之后可以在代码之中利用guppy直接打印出对应各种python类型(list、tuple、dict等)分别创建了多少对象,占用了多少内存。


11from guppy import hpy
import sys
def my_func():
mem = hpy()
with open(sys.argv[1], 'rb') as f:
while True:
buf = f.read(10 * 1024 * 1024)
if buf:
print(mem.heap())
else:
break

运行后,可以看到打印出对应的内存占用数据。

通过上述两种工具guppy与memory_profiler可以很好地来监控python代码运行时的内存占用问题。