因为要读取从大众点评爬下来的一些数据,所以在文件有点大的情况下就造成jupyter notebook在读取的时候直接卡死,出现memory error的情况。
那么如何解决这样的问题呢?掌柜经过一番探索,终于找到解决的办法,先写上解决的代码:
with open('你要读取的文件', 'r',encoding='utf-8') as f:
for line in f:
res = line
print(res)
然后可以查看总耗时是33秒:
别小看这么两行简单的代码,里面蕴含的玄机还是值得思考的。于是掌柜在思考之后,总结了一下关于用Python读取本地较大型文件的各种方法:
- 第一种就是直接读取,使用read()方法,可以看下面的示例代码:
file = '你要读取的文件'
#这里表示用utf-8来编码文件内容
f = open(file,'r',encoding='utf-8')
res = f.read()
print(res)
f.close()
这种方式的好处:直接一次读取文件内容;
但是坏处:
- 一旦文件过大,就会出现memory out(即内存泄漏),然后出现像上面掌柜说的jupyter notebook立马就会卡死的情况;
- 其次,文件读取结束后需要手动写上f.close()才会关闭文件,释放系统内存。
- 第二种方式按行读取,这里分两种情况,如果只读取一行的话,用readline()方法,依然以上面的示例代码来看:
file = '你要读取的文件'
#这里表示用utf-8来编码文件内容
f = open(file,'r',encoding='utf-8')
res = f.readline()
print(res)
f.close()
如果一次逐行读取整个文件,即多行读取的话,使用readlines()方法,就这样写:
file = '你要读取的文件'
#这里表示用utf-8来编码文件内容
f = open(file,'r',encoding='utf-8')
for line in f.readlines():
res = line
print(res)
f.close()
这种其实跟第一种比较类似,区别就是read()是返回的字符串或字节对象,而readlines会返回一个list,且list会直接保存在内存上。所以坏处也和第一种一样,一旦过大就发生内存泄漏!!!
那么有没有更好的方法可以即能逐行读取整个文件,还不会造成内存泄漏呢?
当然有,我们翻看Python的官方文档可以发现,
- 它就是接下来我们要介绍的第三种方法(也就是文章一开始的方法),更“Pythonic”的方法,还是上面的例子,但是代码可以这么写:
with open('你要读取的文件', 'r',encoding='utf-8') as f:
for line in f:
res = line
print(res)
现在根据官方文档来解释一下上面这段代码具体好在哪里!
首先使用with语句的好处 :
(1). 可以在你读写文件结束后自动关闭文件,释放内存,不用再麻烦的写上f.close();
(2). 使用with的话,它会比你使用等价的try-finally 模块速度更快。
而使用for line in f 的方法好处在于 能更高效、快速的读取文件而不引发内存泄漏问题。
PS: 也许你还有疑问,为什么读完文件要写上关闭文件的这一步?
这里再根据官方文档解释一下,有两点原因:
第一:如果你没有明确的告诉Python关闭该文件,那么Python的垃圾回收器就会去毁坏这个文件并关闭这个打开的文件,但是这个文件可能还是会保持一段打开的状态;
第二:不同的Python实现(这里是指Python有很多实现。CPython是最通用的,它是一个用C实现的,被认为是”默认“的实现。还有其他的实现,例如IronPython、Jython 、PyPy、MicroPython等,具体的信息可以参考Python官网。)将会在不同的时间进行清理垃圾,即使后面你用with语句或调用f.close()来关闭了这个文件,但是当你还要再次使用这个文件对象的时候,就会报错如下:
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.
所以经过这个读取文件小小的事情可以看出来,Python语言虽然简单,但是其中的一些细节处理却还是值得我们注意的!!!
最后谢谢阅读,如果有问题,请指出纠正😁。