【问题标题】:Python - Best way to read a file and break out the lines by a delimeterPython - 读取文件并通过分隔符分隔行的最佳方法
【发布时间】: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]

【问题讨论】:

    标签: python file-io generator


    【解决方案1】:

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

    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,',')
    • 当然,假设创建生成器比保留列表更便宜 - 这可能是从文件读取的情况,但如果生成器从数据库中产生行,或者从网络服务器产生数据,我不太确定。
    【解决方案2】:

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

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

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

    我猜只有降低到 C/Cython 级别才能提高速度; str.split 很难被击败,因为它是用 C 编写的,并且列表推导是 AFAIK 中 Python 中最快的循环构造。

    更重要的是,这是非常清晰的 Py​​thonic 代码。除了生成器位之外,我不会尝试对此进行优化。

    【讨论】:

    • 您之前是否尝试过以这种方式将genex 与file 上下文管理器一起使用?
    • 我很喜欢它@larmans 它不会生成元组列表,但我想你可以像这样添加元组函数。 return (tuple(line.split(delim)) for line in f)
    • @MattAlcock:添加了一些关于为什么不构建元组的内容。
    • @IgnacioVazquez-Abrams:我刚刚尝试过,但我很惊讶地发现它不起作用;我希望生成器是一个闭包。哦,好吧。
    猜你喜欢
    • 2010-10-07
    • 2010-10-03
    • 1970-01-01
    • 2020-11-21
    • 1970-01-01
    • 2019-12-07
    • 2011-10-03
    相关资源
    最近更新 更多