【发布时间】:2011-12-06 22:08:13
【问题描述】:
读取文件并用分隔符分隔行的最佳方法是什么。 返回的数据应该是一个元组列表。
这种方法能被打败吗?这可以更快地完成/使用更少的内存吗?
def readfile(filepath, delim):
with open(filepath, 'r') as f:
return [tuple(line.split(delim)) for line in f]
【问题讨论】:
读取文件并用分隔符分隔行的最佳方法是什么。 返回的数据应该是一个元组列表。
这种方法能被打败吗?这可以更快地完成/使用更少的内存吗?
def readfile(filepath, delim):
with open(filepath, 'r') as f:
return [tuple(line.split(delim)) for line in f]
【问题讨论】:
您发布的代码读取整个文件并在内存中构建文件的副本,作为所有文件内容的单个列表,拆分为元组,每行一个元组。既然你问如何使用更少的内存,你可能只需要一个生成器函数:
def readfile(filepath, delim):
with open(filepath, 'r') as f:
for line in f:
yield tuple(line.split(delim))
但是!有一个重大警告!您只能对 readfile 返回的元组进行一次迭代。
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', ',')) 轻松完成。
【讨论】:
newlines_as_tuples = readfile(mydata,',')
可以通过使用生成器而不是列表和列表而不是元组来减少内存使用,因此您不需要一次将整个文件读入内存:
def readfile(path, delim):
return (ln.split(delim) for ln in open(f, 'r'))
不过,您必须依靠垃圾收集器来关闭文件。至于返回元组:如果没有必要就不要这样做,因为列表要快一点,构建元组的成本很小,而且(重要的是)你的行将被分成可变大小的序列,这些序列在概念上是列表。
我猜只有降低到 C/Cython 级别才能提高速度; str.split 很难被击败,因为它是用 C 编写的,并且列表推导是 AFAIK 中 Python 中最快的循环构造。
更重要的是,这是非常清晰的 Pythonic 代码。除了生成器位之外,我不会尝试对此进行优化。
【讨论】:
file 上下文管理器一起使用?