NumPy数组与Python列表命名、赋值行为差异原理解释
NumPy与原生Python列表切片赋值行为差异的底层原理
核心存储结构差异
- 原生Python列表是存储Python对象引用的动态指针数组:列表的每个内存槽位存放的是指向对应Python对象的内存地址,列表本身不直接存储元素的原始数据。
- NumPy的ndarray是面向同构数值数据的连续内存块封装:ndarray对象本身仅存储四类核心元数据:指向原始连续内存块的指针、元素数据类型(
dtype)、数组维度形状(shape)、各维度寻址步长(strides),不会为每个元素单独存储Python对象引用。
切片操作的实现差异
你观察到的行为差异完全来自两者切片逻辑的不同,并没有违反Python"赋值操作不复制数据"的核心规则:
- 对原生Python列表做切片时,会创建一个全新的列表对象,新列表会把原列表切片范围内的所有对象引用复制到自己的内存槽位中。新列表和原列表的槽位是完全独立的,修改原列表某个槽位的引用指向(即给列表元素重新赋值),只会改动原列表自身槽位存储的地址,不会影响新列表槽位中已经存好的旧引用,因此切片出来的列表不会随原数组元素赋值变化。
- 对NumPy数组做切片时,同样不会复制原始数据,只会创建一个全新的ndarray元数据对象:这个新对象的内存指针直接指向原数组底层内存块中对应切片的起始位置,仅根据切片范围调整自身的
shape和strides参数,和原数组完全共享同一块底层原始内存。
对应代码现象解释
复现问题的代码如下:
import numpy as np a = np.array([1, 2, 3, 4]) b = a[1:3] a[1] = 5
- 执行
b = a[1:3]时,b没有复制任何元素值,它的底层指针直接指向a的内存块中索引为1的元素起始地址,可访问范围是连续2个int类型长度的内存段。 - 执行
a[1] = 5时,NumPy会直接把原数组底层内存块对应位置的二进制值修改为5的int类型二进制表示,因为b和a共享同一块内存,读取b的第一个元素时自然会读到修改后的值,最终b的取值为[5, 3]。
如果你需要NumPy切片生成和原数组完全独立的副本,只需要显式调用拷贝方法:
b = a[1:3].copy(),该操作会单独开辟新的内存块存储切片数据,后续修改原数组不会对b产生影响。
内容的提问来源于stack exchange,提问作者Amna Najib
相关产品推荐
相关产品推荐

