list为什么慢:ndarray的内存连续性
我的掌握程度:
先掌握这个答案
内存布局:list=「指针数组」——每个元素是分散在内存各处的Python对象的地址(且每个对象带类型标签、引用计数等开销);ndarray=「连续内存块+单一类型」——一百万个float紧紧挨着。快的三重原因:①CPU缓存友好(连续预取);②无逐对象的解释器开销;③同类型允许SIMD指令批量运算。仍用list的场景:异构元素(字符串+数字+对象混合)、需要频繁的追加/删除(ndarray追加是全量复制)。
先读结论,再核对下方使用边界;数字例题为教学情景,涉及实际行情与规则时请核对原始来源。
从理解到使用
本阶段实战方法 · 一条能复现的数据流水线
把接口返回的数据变成可用于因子研究的面板时,先处理时点、主键与单位。
- 建立数据字典:字段、单位、时区、交易日历、数据源、抓取时间、原始价与复权因子。
- 用(date, code)查重,区分停牌、未上市、窗口不足和真实缺失;不要用向后填充把未来值带回过去。
- 保存原始快照与清洗日志,固定随机种子和环境版本;接口字段变化时先核对官方文档。
使用边界
- 报告期不等于公告可用日,盘后公告通常不能用于当天收盘前决策。
- API输出名为“动态市盈率”时不能直接改名TTM;浏览器练习不支持任意原生库或行情抓取接口。
不懂的术语:标签(Label / Target)
背景 · 从一个困惑说起
同样是装100万个数,list遍历要几百毫秒、ndarray只要几毫秒——百倍差距的根源不在Python"慢",在数据在内存里的"住法"不同。
由此产生的问题
list与ndarray的内存布局差异是什么?为什么连续内存+同类型=百倍速?什么时候仍必须用list?
核心概念
内存布局:list=「指针数组」——每个元素是分散在内存各处的Python对象的地址(且每个对象带类型标签、引用计数等开销);ndarray=「连续内存块+单一类型」——一百万个float紧紧挨着。快的三重原因:①CPU缓存友好(连续预取);②无逐对象的解释器开销;③同类型允许SIMD指令批量运算。仍用list的场景:异构元素(字符串+数字+对象混合)、需要频繁的追加/删除(ndarray追加是全量复制)。
为什么需要它
①理解性能问题的第一性原理:以后看到慢代码,先问「数据住得对不对」而不是「Python是不是不行」;②dtype意识:ndarray还有类型(float64/int32/bool)——错类型既费内存又阻速度(object类型的ndarray≈披着array皮的list);③工程习惯:研究数据的默认容器=ndarray/DataFrame,list只做临时集合。
实际应用场景
面板数据全程用ndarray/DataFrame;dataframe['code']=dataframe['code'].astype('category')省内存90%;把object列转数值列常是内存与速度的双修。
与之前知识的联系
q-301的向量化世界观落到物理层;q-302的广播建立在ndarray结构上;q-366(内存优化)在此有了原理基础。
⚠ 常见误解
- Python慢所以量化该换语言:90%的"慢"来自容器与写法;换语言前先问数据布局——本课的解法成本最低
- ndarray一定比list快:object dtype的ndarray几乎不快;类型统一才有资格快
- for循环写清楚点也行:清楚与快不冲突——向量化代码往往更接近数学公式的样子(q-041对照)
🛠 本题实践任务
用time对比三种容器各跑20次求和的耗时(list循环/转ndarray/原生ndarray),并检查你的某个DataFrame的dtypes——找出object列并思考它该是什么类型。
💡 提示(先自己想5分钟再看)
df.dtypes一目了然;股票代码列常被读成object——category或int都更优。
✅ 验收标准
三行耗时数字+你数据里的object列清单。「容器意识」从此植入。
在你的浏览器内运行。首次需联网下载 Python;支持标准库和 Pyodide 提供的 NumPy、pandas 等包。行情抓取、系统文件访问和部分第三方库请在本地环境运行。输出自检仅比对结果关键词,不代替逻辑审查。
Python · 独立工作线程 · 执行限时10秒
🤖 AI导师
导师会先检查你的前置知识(向量化思维),再用案例、公式和代码帮助理解。此功能需要先启用服务。