【问题标题】:Is .append() time-consuming?.append() 耗时吗?
【发布时间】:2011-08-23 21:17:26
【问题描述】:

这些天我一直在处理巨大的文本文件。有时我需要删除行。 我的做法如下:

f=open('txt','r').readlines()
list=[]
for line in f:
    if blablablabla:
       list.append(line)

我知道对于大文件,.readlines() 是限速步骤,但是 .append() 步骤呢?在 readlines 之后追加是否会花费大量额外的时间? 如果是这样,也许我应该想办法直接删除我不想要的行,而不是附加我想要的行。

谢谢

【问题讨论】:

    标签: python append readlines


    【解决方案1】:

    如果您稍后要过滤它,为什么要使用readlines() 读取整个文件?只需遍历它即可保存您想要保留的行。您可以改为使用列表理解将其减少到几行:

    with open('txt', 'r') as f:
        myList = [ line for line in f if blablablabla ]
    

    【讨论】:

    • 好吧,使用 readlines,您已经将 整个 文件读入了一个列表。然后根据其内容创建另一个过滤列表。您不需要使用 readlines 预先使用整个文件,因此最好根本不使用它(或者更改您的算法以从该列表中删除不需要的行)。担心追加的速度是不必要的,它真的不应该对你的代码速度有任何影响。如果您真的想深入了解它,您应该对其进行分析并找出自己。
    • 也许,取决于文件的大小。列表理解很快。此外,您不会一次将整个文件加载到内存中,这样可以保持性能。
    • @wang,即使没有任何行被过滤掉,你至少可以节省制作整个列表的额外副本。
    • thx...但是“取决于大小”是什么意思?我的输入文件超级大,有 50,000,000 行……那么对于这么大的文件,列表理解仍然会快得多?
    • @wang:只要文件不是很大,使用 readlines 就可以了。 5000 万行绝对是非常大的,所以你应该不惜一切代价避免它。您还没有提到您对这些行的具体操作,但您可能希望避免将它们一起放入一个列表中。如果您打算最终将它们写回文件,最好将它们写回文件(如 sunjay 的回答)或在循环文件时立即处理它们。
    【解决方案2】:

    作为一般提示,请改为执行此操作,无需在迭代之前先读取完整文件...

    with open('txt') as fd:
        for line in fd:
            if blablabla:
                my_list.append(line)
    

    并且不要将列表称为“列表”...

    【讨论】:

    • array 也不是一个好的选择(虽然比 list 好),因为还有 array 模块
    【解决方案3】:

    您应该使用列表推导来代替 Jeff 的回答。根据您需要如何处理数据,您或许可以改用生成器表达式。

    回答关于 append() 的问题

    Python 列表在末尾预先分配了一些额外的空间。这意味着追加非常快 - 直到您用完预分配的空间。每当列表扩展时,都会分配一个新的内存块并将所有引用复制到它。随着列表的增长,额外预分配空间的大小也会增长。这样做是为了使 append 摊销 O(1)。即追加的平均时间是快速且恒定的

    【讨论】:

      【解决方案4】:

      在这篇文章中,我试图解释列表的工作方式以及为什么 append 不是 非常 昂贵的。我还在底部发布了一个解决方案,您可以使用它来删除行。

      Python 的列表结构就像一个节点网络:

      >>> class ListItem:
              def __init__(self, value, next=None):
                  self.value = value
                  self.next = next
              def __repr__(self):
                  return "Item: %s"%self.value
      
      
      >>> ListItem("a", ListItem("b", ListItem("c")))
      Item: a
      >>> mylist = ListItem("a", ListItem("b", ListItem("c")))
      >>> mylist.next.next
      Item: c
      

      因此,append 基本上就是这样的:

      ListItem(mynewvalue, oldlistitem)
      

      Append 没有太多开销,但另一方面,insert() 需要您重新构建整个列表,因此会花费更多时间。

      >>> from timeit import timeit
      >>> timeit('a=[]\nfor i in range(100): a.append(i)', number=1000)
      0.03651859015577941
      >>> timeit('a=[]\nfor i in range(100): a.insert(0, i)', number=1000)
      0.047090002177625934
      >>> timeit('a=[]\nfor i in range(100): a.append(i)', number=10000)
      0.18015429656996673
      >>> timeit('a=[]\nfor i in range(100): a.insert(0, i)', number=10000)
      0.35550057300308424
      

      如您所见,插入要慢得多。如果我是你,我会删除你不需要的行,马上写回去。

      with open("large.txt", "r") as fin:
          with open("large.txt", "w") as f:
              for line in fin:
                  if myfancyconditionismet:
                      # write the line to the file again
                      f.write(line + "\n")
                  # otherwise it is gone
      

      有我的解释和解决办法。

      -Sunjay03

      【讨论】:

      • 我 99% 确定您对 Python 列表的解释不正确。它们被实现为可调整大小的数组,而不是链表。您绘制的数据结构的索引性能很差(例如somelist[43])。追加速度很快,因为数组末尾通常有空间用于另一个项目,而插入速度很慢,因为它需要将所有后续元素移动到一个位置上才能为插入的元素腾出空间。
      【解决方案5】:

      也许你想把它全部拉到内存中,然后对其进行操作。也许一次在一条线上操作更有意义。从您的解释中不清楚哪个更好。

      无论如何,无论您采用哪种方法,这里都有相当标准的代码:

      # Pull one line into memory at a time
      with open('txt','r') as f:
          lineiter = (line for line in f if blablablabla)
          for line in lineiter:
              # Do stuff
      
      # Read the whole file into memory then work on it
      with open('txt','r') as f:
          lineiter = (line for line in f if blablablabla)
          mylines = [line for line in lineiter]
      

      如果您走前一条路线,我建议您阅读生成器。 Dave Beazley 有一篇很棒的关于生成器的文章,名为 "Generator Tricks for Systems Programmers"。强烈推荐。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-21
        • 2011-11-29
        • 1970-01-01
        • 1970-01-01
        • 2014-01-08
        • 2017-04-14
        • 2013-09-10
        • 1970-01-01
        相关资源
        最近更新 更多