NumPy整型切片返回视图,零拷贝、恒定耗时、运算向量化;Python列表切片创建新对象,线性耗时、解释器循环执行。关键依赖dtype明确、内存连续及C层直接调用。

整型切片不拷贝数据,只改视图元信息
NumPy 的整型切片(如 arr[100:500])返回的是原数组的“视图”(view),不是新数组。它只修改 shape、strides 和 data 起始偏移量,内存地址没变,零拷贝。而 Python 列表切片(如 lst[100:500])必须分配新 list 对象,逐个复制引用,再维护引用计数——光是内存分配和指针搬运就吃掉大量时间。
-
arr[100:500].flags.owndata返回False表示它是视图;lst[100:500] is lst永远为False - 大数组切片时,NumPy 视图创建耗时基本恒定(纳秒级);列表切片耗时随长度线性增长
- 注意:
arr[::2]这类非连续切片虽也是视图,但arr[::2].flags.c_contiguous通常为False,后续计算可能降速
切片后运算仍走 C 层,不触发 Python 解释器
对 NumPy 视图做 +、np.sum() 等操作,底层仍直接传原始内存块地址+新 shape/strides 给 C 函数处理。Python 列表切片后得到的是全新 list,任何运算都得走 Python 字节码循环——每次加法都要查类型、调 __add__、封装新 int 对象。
- 验证方式:运行
arr[100:500] + 1,它调用的是PyArray_GenericBinaryFunction,不是 Python 的list.__add__ - 哪怕切片很小(如
arr[0:10]),只要原数组 dtype 是int32或float64,运算仍向量化;而[x+1 for x in lst[0:10]]仍是 10 次解释器开销 - 陷阱:
arr.tolist()[100:500]看似“先转再切”,实则已全量拷贝成 list,彻底失去 NumPy 优势
dtype 固定让步长可预测,CPU 缓存友好
NumPy 整型切片能快的前提是数组 dtype 明确且连续。比如 np.arange(1000000, dtype=np.int32),每个元素占 4 字节,arr[100:500] 的内存跨度就是 (500-100)*4=1600 字节,CPU 预取一次就能拉满缓存行;而列表切片后得到的是一堆散落在堆里的 int 对象指针,地址随机,缓存命中率极低。
- 检查连续性:
arr.flags.c_contiguous必须为True才能保证切片后仍高效 - 避免
np.array([1, 2, "hello"])这种混合输入——会退化成dtype=object,切片后所有运算回退到 Python 循环,比原生 list 还慢 - 小数组(
别被“看起来一样”的语法骗了
写 arr[100:500] 和 lst[100:500] 表面一样,但底层机制完全不同:前者是内存地址+偏移+步长的 C 结构体重组,后者是 PyObject* 数组的深拷贝。这种差异在循环里放大——比如用切片做滑动窗口统计,NumPy 可以 for i in range(n): window = arr[i:i+100]; do_something(window),每次切片开销几乎为零;而列表版本每次都在 malloc 新空间。
立即学习“Python免费学习笔记(深入)”;
真正容易被忽略的点是:切片本身不慢,慢的是你把它转成 list、混入 object dtype、或在非连续视图上反复做 reduce 操作。性能不在语法糖,而在你是否让内存保持连续、类型保持单一、运算保持纯数值映射。


















