专栏首页HappenLee的技术杂谈Python读取大文件的"坑“与内存占用检测

Python读取大文件的"坑“与内存占用检测

python读写文件的api都很简单,一不留神就容易踩”“。笔者记录一次踩坑历程,并且给了一些总结,希望到大家在使用python的过程之中,能够避免一些可能产生隐患的代码。

1.read()与readlines():

随手搜索python读写文件的教程,很经常看到read()与readlines()这对函数。所以我们会常常看到如下代码:

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,当文件大小大于可用内存时,自然会发生内存溢出的错误。

read方法

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

readlines方法

2.正确的用法:

在实际运行的系统之中如果写出上述代码是十分危险的,这种”坑“十分隐蔽。所以接下来我们来了解一下正确用,正确的用法也很简单,依照API之中对函数的描述来进行对应的编码就OK了:

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

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)

3.内存检测工具的介绍:

对于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** 就可以了解函数每一步代码的内存占用了

依次列出每一步代码的内存占用情况

guppy

依样画葫芦,仍然是通过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

如下图所示,可以看到打印出对应的内存占用数据:

python代码详细的内存占用情况

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

4.小结:

python是一门崇尚简洁的语言,但是正是因为它的简洁反而更多了许多需要仔细推敲和思考的细节。希望大家在日常工作与学习之中也能多对一些细节进行总结,少踩一些不必要的“坑”。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux 程序设计1:深入浅出 Linux 共享内存

    说到共享内存,有过操作系统学习的童靴应该十分熟悉,往往聊到进程之间通信的4种方式时就能脱口而出(面试最常见的问题之一啊,哈哈哈~~):

    HappenLee
  • C++雾中风景番外篇3:GDB与Valgrind ,调试代码内存的工具

    CoreDump时一个二进制的文件,进程发生错误崩溃时,内核会产生一个瞬时的快照,记录该进程的内存、运行堆栈状态等信息保存在core文件之中。做个简单的类比,c...

    HappenLee
  • 事务与隔离级别------《Designing Data-Intensive Applications》读书笔记10

    1983年,Andreas Reuter and Theo Härder 提出了事务之中重要的四个特性:

    HappenLee
  • 中国团队再次称雄AI大赛,微软谷歌FB都甩在身后

    李林 假装发自 威尼斯 量子位 出品 | 公众号 QbitAI 又一次!中国团队拿下一项AI赛事的多个大奖! 8天的计算机视觉顶会ICCV 2017在威尼斯悄然...

    量子位
  • javascript坐标:event.x、event.clientX、event.offsetX、event.screenX 用法

    clientX 设置或获取鼠标指针位置相对于窗口客户区域的 x 坐标,其中客户区域不包括窗口自身的控件和滚动条。

    大道七哥
  • 《2016小明滚出去》之程序猿版

    小明是一个霸占广大70后、80后、90后及00后小学语文、数学课本长达三四十年之久的人物,他不辞辛苦的活跃在各种应用题、作文中,是小学课本甚至是题海中当之无愧的...

    用户1667431
  • 六步教你如何用PADS进行PCB设计?

      在使用PADS进行PCB设计的过程中,需要对印制板的设计流程以及相关的注意事项进行重点关注,这样才能更好的为工作组中的设计人员提供系统的设计规范,同时也方便...

    用户6754675
  • Spring mvc 内置编码过滤器原理解析

    java404
  • Python 3.x中的编码和解码问题

    最近在极客学院学习爬虫,老师用的是2.x版本,而我电脑里的版本是3.x,于是在网页上查找在输出中文时如何正确输出。 我原以为2.x 3.x在很多语法上没什么...

    py3study
  • Python3 初学实践案例(1)按条件生成复杂密码

    Python3 初学实践案例(1)按条件生成复杂密码 最近事情太多,golang 学的东西忘记得差不多了。想想不如学习一下 python 吧~,反正我是一前端,...

    FungLeo

扫码关注云+社区

领取腾讯云代金券