python读写文件的api都很简单,一不留神就容易踩”坑“。笔者记录一次踩坑历程,而且给了一些总结,但愿到你们在使用python的过程之中,可以避免一些可能产生隐患的代码。python
随手搜索python读写文件的教程,很常常看到read()与readlines()这对函数。因此咱们会经常看到以下代码:api
with 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?
咱们首先来看看这两个方法:性能
当默认参数size=-1时,read方法会读取直到EOF,当文件大小大于可用内存时,天然会发生内存溢出的错误。
学习
一样的,readlines会构造一个list。list而不是iter,因此全部的内容都会保存在内存之上,一样也会发生内存溢出的错误。
测试
在实际运行的系统之中若是写出上述代码是十分危险的,这种”坑“十分隐蔽。因此接下来咱们来了解一下正确用,正确的用法也很简单,依照API之中对函数的描述来进行对应的编码就OK了:编码
若是是二进制文件推荐用以下这种写法,能够本身指定缓冲区有多少byte。显然缓冲区越大,读取速度越快。code
with open(file_path, 'rb') as f: while True: buf = f.read(1024) if buf: sha1Obj.update(buf) else: break
而若是是文本文件,则能够用readline方法或直接迭代文件(python这里封装了一个语法糖,两者的内生逻辑一致,不过显然迭代文件的写法更pythonic )每次读取一行,效率是比较低的。笔者简单测试了一下,在3G文件之下,大概性能和前者差了20%.对象
with 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代码的内存占用问题,对于代码进行内存监控十分必要。这里笔者这里推荐两个小工具来检测python代码的内存占用。
####memory_profiler
首先先用pip安装memory_profiler
pip install memory_profiler
memory_profiler是利用python的装饰器工做的,因此咱们须要在进行测试的函数上添加装饰器。
from 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**
就能够了解函数每一步代码的内存占用了
依样画葫芦,仍然是经过pip先安装guppy
pip install guppy
以后能够在代码之中利用guppy直接打印出对应各类python类型(list、tuple、dict等)分别建立了多少对象,占用了多少内存。
from 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代码运行时的内存占用问题。
python是一门崇尚简洁的语言,可是正是由于它的简洁反而更多了许多须要仔细推敲和思考的细节。但愿你们在平常工做与学习之中也能多对一些细节进行总结,少踩一些没必要要的“坑”。