重复附加到一个大列表(Python 2.6.6)

Posted

技术标签:

【中文标题】重复附加到一个大列表(Python 2.6.6)【英文标题】:Repeatedly appending to a large list (Python 2.6.6) 【发布时间】:2011-08-15 14:08:54 【问题描述】:

我有一个项目,我正在通过串行端口从微控制器读取 ASCII 值(如下所示:AA FF BA 11 43 CF 等) 输入很快(38 个两个字符集/秒)。 我正在接受此输入并将其附加到所有测量的运行列表中。

大约 5 小时后,我的列表已增长到大约 855000 个条目。

我了解到,列表越大,列表操作就越慢。我的意图是让这个测试运行 24 小时,这应该会产生大约 300 万个结果。

有没有比 list.append() 更有效、更快的方法来追加到列表?

谢谢大家。

【问题讨论】:

“我知道列表越大,列表操作越慢。” [citation needed] 您是否尝试运行 24 小时并发现问题?有什么问题? @Matt,见***.com/questions/2473783/… @Mark:很有趣。这是一个bug,现在已经修复了。 @Mark:这个问题的答案是 Python GC 中存在一个错误,因此问题的解决方案是禁用 GC,而不是使用不同的算法。 【参考方案1】:

我了解到,列表越大,列表操作就越慢。

一般情况下并非如此。 Python 中的列表,尽管有名称,但不是链表,而是数组。数组上有一些 O(n) 操作(例如复制和搜索),但您似乎没有使用任何这些操作。作为一个经验法则:如果它被广泛使用和惯用,一些聪明的人就会去选择一种聪明的方式来做这件事。 list.append 是一个广泛使用的内置函数(底层 C 函数也用于其他地方,例如列表推导)。如果有更快的方法,它已经在使用了。

正如您在检查the source code 时将看到的那样,列表正在过度分配,即当它们调整大小时,它们为一个项目分配了超过所需的数量,因此可以附加下 n 个项目而无需再次调整大小(即 O( n))。增长不是恒定的,它与列表大小成正比,因此随着列表变大,调整大小变得越来越少。这是来自listobject.c:list_resize 的确定过度分配的sn-p:

/* This over-allocates proportional to the list size, making room
 * for additional growth.  The over-allocation is mild, but is
 * enough to give linear-time amortized behavior over a long
 * sequence of appends() in the presence of a poorly-performing
 * system realloc().
 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
 */
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);

正如 Mark Ransom 所指出的,较旧的 Python 版本(

【讨论】:

理论上正确答案,但实际情况要复杂得多。除非你自己测量过并且知道它会在最近的 Python 版本中得到修复 - 请参阅 ***.com/questions/2473783/… 感谢您的信息。我不知道这个。【参考方案2】:

您可能需要考虑的一件事是在收集数据时将数据写入文件。我不知道(或真正关心)它是否会影响性能,但它有助于确保在电源突然中断时不会丢失所有数据。获得所有数据后,您可以将其从文件中吸出,并将其放入列表、数组或 numpy 矩阵或其他任何处理中。

【讨论】:

+1:确实,写入文件是一个非常糟糕的设计。【参考方案3】:

追加到 python 列表的成本是恒定的。它不受列表中项目数量的影响(理论上)。实际上,一旦内存不足并且系统开始交换,追加到列表会变慢。

http://wiki.python.org/moin/TimeComplexity

了解为什么您实际上将内容附加到列表中会很有帮助。你打算如何处理这些物品。如果您不需要所有这些,您可以构建一个环形缓冲区,如果您不需要进行计算,您可以将列表写入文件等。

【讨论】:

列表不断增长的原因是我确实需要稍后(在捕获之后)对元素进行一些数学运算,并将其作为 csv 文件传递​​(以便以后在 Matlab 中进行操作)。跨度> 【参考方案4】:

首先,每秒38个双字符集,1个停止位,8个数据位,无奇偶校验,只有760波特,一点都不快。

但无论如何,我的建议是,如果您担心列表过大/不想使用一个庞大的列表,只需在列表达到一定大小后将其存储在磁盘上并开始一个新列表,重复直到你获得所有数据,然后在你完成接收数据后将所有列表合并为一个。

虽然您可以完全跳过子列表,而只是按照 nmichaels 的建议,将数据写入一个文件,并使用一个小的循环缓冲区来保存尚未写入的接收数据。

【讨论】:

【参考方案5】:

如果您知道数组的长度并且可以将十六进制代码转换为整数,那么使用 numpy 可能会更快:

import numpy
a = numpy.zeros(3000000, numpy.int32)
for i in range(3000000):
   a[i] = int(scanHexFromSerial(),16)

这将为您留下一个整数数组(您可以使用 hex() 将其转换回十六进制),但根据您的应用程序,这可能对您同样有效。

【讨论】:

以上是关于重复附加到一个大列表(Python 2.6.6)的主要内容,如果未能解决你的问题,请参考以下文章

Python - 将函数附加到列表[重复]

附加到 Python 字典中的列表 [重复]

Python - 如何创建一个空的numpy数组并附加到它,如列表[重复]

附加到python中的空列表时出错[重复]

Python-在while循环期间将列表附加到列表-结果与预期不符[重复]

Python:将 lambda 函数附加到列表 [重复]