zoukankan      html  css  js  c++  java
  • Python读取大文件的"坑“与内存占用检测

    Python高级教程- Python进阶|Scrapy教程|Python高级|Python深入 (pythontab.com)

    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方法
    read([size])方法从文件当前位置起读取size个字节,若无参数size,则表示读取至文件结束为止,它范围为字符串对象

    同样的,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
      

    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
      

  • 相关阅读:
    Webpack的组成部分 半 详解
    代码简洁之道:编写干净的 React Components & JSX
    大体量点位置数据动态聚合Binning可视化效果
    树莓派FRP服务器自启动失败原因及解决办法
    nopCommerce自学笔记(一、环境搭建)-BigIcicle
    NopCommerce4.3中文版资源
    利用阿里云防勒索备份文件->ibdata 和 frm 文件恢复 MySQL 数据库
    ubuntu mysql设置sql_mode
    Ubuntu16.04 中PHP7.0 安装pdo_mysql 扩展
    相邻两个生产计划之间的衔接问题
  • 原文地址:https://www.cnblogs.com/huaobin/p/15675692.html
Copyright © 2011-2022 走看看