python学习_python如何读取大文件
楔子

在一次面试过程中,主考官提出了问题:如何使用python读取大文件
而我一般知道:
1.一般读取、操作文件都是整体读取到内存中,所以在内存不够大的时候,读取大文件会造成非常耗时+内存溢出的问题
2.我在工作场景中一般读取文件都是使用pd.read_csv()这个pandas的命令,因此只回答了这个方法内置了chunksize参数,可以用于按照指定行数读取文件(返回一个可迭代对象,每次迭代一个分块),每个分块都是dataframe,且每个dataframe的行数是chunksize。
因此,今天专门去搜索了相关的资料,总结如下。
python读取大文件最常用的两种方法
通常面对的大文件有两种形式:
-
多行大文件,一般里面存放的是字符串(普通文本)
-
单行大文件,一般里面存放的是字节串(二进制流)
因此,在经过面向搜索引擎编程后,总结了最有针对性+最优+最简的两种方法分别针对两种情况:
针对多行大文件

使用open的基础方法:open打开的是一个按行迭代的可迭代对象,通过对其进行遍历,即可一行一行的读取文件,避免一次占用较大内存,底层内置了io缓存和内存的管理,无需使用者关心。
with句柄作为上下文管理器,负责打开和关闭文件(包括在内部块中引发异常时)
# for line in f 会将文件对象 f 视为一个可迭代的数据类型,自动使用 IO 缓存和内存管理
with open('filename', 'rt', encoding='utf-8') as f:
for line in f:
do_something(line)
针对单行大文件

上面的open方法解决不了的一个问题是:当大文件只有一行时该怎么办
因此,此方法借助文件对象的read(size=1024*8)方法,按数据的大小进行分块读取,size的单位是比特:bytes
借助patial方法和iter方法更pythonic的实现这个功能:
- functools.partial(io.read,size)用来创建一个每次被调用时从文件中读取固定数目字节的可调用对象,他接收两个参数,一个是函数,一个是函数参数,返回的是每次调用此函数的返回对象。因此,一般使用返回迭代器的函数+参数作为partial的函数输入。
- iter()函数有一个鲜为人知的特性就是,如果你给它传递一个可调用对象和一个标记值,它会创建一个迭代器。这个迭代器会一直调用传入的可调用对象直到它返回标记值为止,这时候迭代终止。在下面的例子中,iter循环将不断返回 fp.read(block_size) 调用结果,直到其为 '' 时终止,标记值b''就是当到达文件结尾时的返回值。
- PS.如果总文件大小不是块大小的整数倍的话,最后一个返回元素的字节数会比期望值少。
# 生成器函数:分块读取文件内容,使用 iter+partial 函数
def chunked_file_reader(file, block_size=1024 * 8):
records = iter(partial(file.read, block_size), '')
for chunk in records:
yield chunk
# 分数据块读取文件
with open('filename', 'rb', encoding='utf-8') as f:
for chunk in chunked_file_reader(f):
do_something(chunk)
知识补充:
partial函数
如果需要减少某个函数的参数个数,你可以使用 functools.partial() 。 partial() 函数允许你给一个或多个参数设置固定的值,减少接下来被调用时的参数个数。
partial()会固定某些参数并返回一个新的callable对象。这个新的callable接受未赋值的参数, 然后跟之前已经赋值过的参数合并起来,最后将所有参数传递给原始函数。
partial的内部机制和装饰器一样利用了函数式编程的闭包特性。简而言之,partial作为一个函数式编程中的高阶函数,其功能就是为某个已经存在的函数对象提供了一种简洁的绑定函数参数的方式。对于关键字参数,相当于提供默认值,对位置参数相当于冻结参数。
重要的是,这种参数绑定,不是在函数定义阶段(比如默认值参数),而是借助外部工具partial来进行参数的绑定,返回一个参数缩减的特化版本,这个绑定过程可以发生在编写代码的阶段(简单的得到一个特化函数),甚至可以发生在运行时,通过配置文件或者交互输入解析待绑定的参数,来动态的生成特定功能的函数。
PS. partial做到了开放封闭原则:对外开放,对内(修改)封闭
参考:https://python3-cookbook.readthedocs.io/zh_CN/latest/c07/p08_make_callable_with_fewer_arguments.html
参考:https://zhuanlan.zhihu.com/p/47124891
iter函数
iter()函数有两种用法,一种是传一个参数,一种是传两个参数。结果都是返回一个iterator对象。
所谓的iterator对象,就是有个next()方法的对象。next方法的惯例或约定(convention)是,每执行一次就返回下一个值(因此它要自己记录状态,通常是在iterator对象上记录),直到没有值的时候raiseStopIteration。
传1个参数:参数collection应是一个可迭代的集合对象,支持迭代协议(即定义有__iter__()函数),或者支持序列访问协议(即定义有getitem()函数),否则会返回TypeError异常。
传2个参数:当第二个参数sentinel出现时,参数callable应是一个可调用对象(实例),即定义了__call__()方法,当枚举到的值等于哨兵时,就会抛出StopIteration异常。
参考:https://blog.csdn.net/sxingming/article/details/51479039
总结
在搜索资料的过程中,发现了读取大文件还有很多其他写法,但是归根到底的就这两类情况,其他的都是这两类的变种。
如果有其他方法,还请大神在评论区指出,不胜感激涕零。

浙公网安备 33010602011771号