Python - 读取文件并通过分隔符分隔行的最佳方法

时间:2011-10-13 12:10:19

标签: python file-io generator

读取文件并通过分隔符分隔行的最佳方法是什么。 返回的数据应该是元组列表。

这种方法可以被打败吗?这可以更快/使用更少的内存吗?

def readfile(filepath, delim):
    with open(filepath, 'r') as f:
        return [tuple(line.split(delim)) for line in f]

2 个答案:

答案 0 :(得分:14)

您发布的代码读取整个文件并在内存中构建该文件的副本,作为分成元组的所有文件内容的单个列表,每行一个元组。由于您询问如何使用更少的内存,您可能只需要一个生成器函数:

def readfile(filepath, delim): 
    with open(filepath, 'r') as f: 
        for line in f:
            yield tuple(line.split(delim))

BUT!有一个重要的警告!您只能迭代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!

BAM!这是生成器和列表之间的巨大差异。现在在代码的这一点上,生成器已被完全消耗 - 但没有引发特殊异常,for循环什么都不做,继续,静默!

在许多情况下,您将获得的列表仅迭代一次,在这种情况下,将readfile转换为生成器就可以了。但是如果你想要的是一个更持久的列表,你将多次访问它,那么只使用一个生成器会给你带来问题,因为你只能迭代生成器一次。

我的建议?使readlines成为一个生成器,这样在它自己的世界观点中,它只会产生文件的每个增量位,既美观又节省内存。将保留数据的负担放在调用者身上 - 如果调用者需要多次引用返回的数据,则调用者可以简单地从生成器构建自己的列表 - 使用list(readfile('file.dat', ','))在Python中轻松完成。 / p>

答案 1 :(得分:3)

使用生成器而不是列表和列表而不是元组可以减少内存使用,因此您不需要立即将整个文件读入内存:

def readfile(path, delim):
    return (ln.split(delim) for ln in open(f, 'r'))

但是,您必须依赖垃圾收集器来关闭文件。至于返回元组:如果没有必要就不要这样做,因为列表的速度要快一点,构造元组的成本很小,而且(重要的是)你的线将被分成可变大小的序列,这些是概念列表。

我想,只有降低到C / Cython水平才能提高速度; str.split很难被击败,因为它是用C语言编写的,而列表推导是AFAIK是Python中最快的循环结构。

更重要的是,这是非常清晰的Pythonic代码。除了发电机位之外,我不会尝试优化它。