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

append() 会变慢?关键问题不在于 append() 这个操作本身有多慢,而是 Python 列表在容量不足时,会触发一次扩容机制。CPython 的实现里,每次扩容大约是 1.125 倍——意思是当当前容量不够用了,解释器会重新申请一块更大的内存空间,然后把旧元素一个个拷贝过去。假设你循环了几万次,扩容可能发生几十次,每次拷贝都伴随着内存分配的开销。累计下来的耗时,早就不是线性增长那么简单了。
还有一个更隐蔽的陷阱:如果循环体里还顺手做了字符串拼接、字典构造、或者临时对象的创建,这些操作会显著增加垃圾回收的压力。GC 被频繁唤醒,整体执行节奏自然就被拖慢了。
预分配的前提是你得提前知道最终要装多少东西。如果能确定行数——比如遍历一个固定长度的 range、zip、或者读一个已知行数的 CSV——直接上 [None] * n 是最省事的做法。如果长度未知但逻辑上可以推导出来,列表推导式是更好的选择,解释器会在内部自动优化成一次预分配加逐行填充,比手动 append() 快得多,也更符合 Python 的风格。
n:results = [None] * n,然后 results[i] = (x, y, z) 直接按索引赋值[(f(x), g(x)) for x in data][[0] * n] 这种写法——它生成的其实是同一个子列表的多个引用,你改一个,其他全跟着变,后果可想而知pd.DataFrame() 这个构造函数有“脾气”,不同输入结构的性能表现天差地别。tuple 列表或 list 列表速度最快;如果传的是 dict 列表,pandas 需要额外做字段对齐,开销就上去了;要是用 pd.concat 拼接多个 Series,性能会断崖式下跌。
list[tuple] 或 list[list],并且保证列的顺序与目标 DataFrame 的列名严格一致columns=,否则 pandas 在遇到混合类型数据时容易自动推成 object,后续计算容易出问题None 或 np.nan,建议构造完后立刻用 df.astype(...) 显式设定 dtype,防止后续操作意外把整列升格成 objectdata = [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 列表里每个元素是不是长得一模一样。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8