这段代码揭示了一个很经典的问题——在循环里不断用 append() 往列表里塞东西,跑着跑着突然变慢,尤其是在处理几万甚至几十万行数据的时候。很多人都遇到过这个场景,第一反应是“是不是 append() 本身效率有问题?”但从底层实现来看,真正的原因可能和你想的不太一样。

Python怎样避免在循环中不断追加数据_预分配列表收集后统一转换为DF

为什么循环中 append() 会变慢?

关键问题不在于 append() 这个操作本身有多慢,而是 Python 列表在容量不足时,会触发一次扩容机制。CPython 的实现里,每次扩容大约是 1.125 倍——意思是当当前容量不够用了,解释器会重新申请一块更大的内存空间,然后把旧元素一个个拷贝过去。假设你循环了几万次,扩容可能发生几十次,每次拷贝都伴随着内存分配的开销。累计下来的耗时,早就不是线性增长那么简单了。

还有一个更隐蔽的陷阱:如果循环体里还顺手做了字符串拼接、字典构造、或者临时对象的创建,这些操作会显著增加垃圾回收的压力。GC 被频繁唤醒,整体执行节奏自然就被拖慢了。

预分配列表的两种可靠做法

预分配的前提是你得提前知道最终要装多少东西。如果能确定行数——比如遍历一个固定长度的 rangezip、或者读一个已知行数的 CSV——直接上 [None] * n 是最省事的做法。如果长度未知但逻辑上可以推导出来,列表推导式是更好的选择,解释器会在内部自动优化成一次预分配加逐行填充,比手动 append() 快得多,也更符合 Python 的风格。

从预分配列表到 DataFrame 的高效转换

pd.DataFrame() 这个构造函数有“脾气”,不同输入结构的性能表现天差地别。tuple 列表或 list 列表速度最快;如果传的是 dict 列表,pandas 需要额外做字段对齐,开销就上去了;要是用 pd.concat 拼接多个 Series,性能会断崖式下跌。

data = [None] * len(items)for i, item in enumerate(items):    data[i] = (item.id, item.name, item.score)df = pd.DataFrame(data, columns=['id', 'name', 'score'])

什么情况下不该预分配?

预分配不是万能的。如果你的循环逻辑里需要动态判断“要不要追加”——比如过滤、异常跳过、嵌套条件——硬套预分配只会让代码变得又难读又容易出错。你得额外维护一个计数器,处理那些没填数据的空位,最后还得手动切片去掉 None。代价太大了。

这种情况下,更务实的做法是老老实实用 append(),但把构造 DataFrame 的动作拖到循环结束之后一次性完成。关键是要确保收集到的数据结构统一、类型稳定,别搞出“dict 里面嵌套 list 里面再套 tuple”这种混乱结构。

说实话,真正影响性能的从来不是 append 这一行代码,而是数据结构混乱、类型不稳定、以及多余的中间转换。与其死盯着预分配不放,不如先检查一下你的 data 列表里每个元素是不是长得一模一样。

本文转载于:https://www.php.cn/faq/2322965.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。