Python - 读取文件并按分隔符分割行的最佳方法
读取文件的最佳方法是什么?并且要根据某个分隔符将行分开。返回的数据应该是一个元组的列表。
这个方法还有更好的替代方案吗?有没有更快或者更省内存的方法?
def readfile(filepath, delim):
with open(filepath, 'r') as f:
return [tuple(line.split(delim)) for line in f]
2 个回答
使用生成器代替列表,使用列表代替元组,可以减少内存的使用,这样就不需要一次性把整个文件都读到内存里。
def readfile(path, delim):
return (ln.split(delim) for ln in open(f, 'r'))
不过,你得依靠垃圾回收器来关闭文件。至于返回元组:如果没有必要的话就别这么做,因为列表的速度稍微快一点,构建元组的成本很小,而且(更重要的是)你的行会被分割成可变大小的序列,这在概念上就是列表。
要提高速度,可能只能深入到C/Cython的层面;str.split
的性能很难超越,因为它是用C写的,而列表推导式在Python中是我所知道的最快的循环结构。
更重要的是,这段代码非常清晰且符合Python的风格。我不会尝试对这段代码进行优化,除了生成器的部分。
你发的代码会把整个文件读进来,然后在内存里创建一个文件内容的副本,所有内容都分成一个个元组,每行一个元组。既然你想要减少内存的使用,其实你只需要一个生成器函数:
def readfile(filepath, delim):
with open(filepath, 'r') as f:
for line in f:
yield tuple(line.split(delim))
但是!这里有个大问题!你只能遍历一次生成的元组。
lines_as_tuples = readfile(mydata,','):
for linedata in lines_as_tuples:
# do something
到这里为止,这没什么问题,生成器和列表看起来是一样的。但假设你的文件里有很多浮点数,而你在遍历文件的时候要计算这些数的平均值。你可以用“# do something”这段代码来计算总和和数字的个数,然后算出平均值。但如果你想再遍历一次,这次是找出每个值与平均值的差,你可能会想再加一个for循环:
for linedata in lines_as_tuples:
# do another thing
# BUT - this loop never does anything because lines_as_tuples has been consumed!
哇!这就是生成器和列表之间的一个大区别。到这段代码的时候,生成器已经被完全用完了——但并不会抛出什么特别的异常,for循环就会默默地什么都不做,继续往下执行!
在很多情况下,你得到的列表只会遍历一次,这样把readfile转换成生成器是没问题的。但如果你想要一个更持久的列表,需要多次访问,那么仅仅使用生成器就会出问题,因为你只能遍历生成器一次。
我的建议是?把readlines做成一个生成器,这样它在自己的小世界里,每次只返回文件的一小部分,既节省内存又高效。如果调用者需要多次引用返回的数据,那就让调用者自己从生成器构建一个列表——在Python中可以很简单地用 list(readfile('file.dat', ','))
来实现。