
numpy 通过 `array.data`(底层为 memoryview)记录实际内存起始地址,结合 `strides` 和 `shape` 解释数据布局;切片视图不修改 `base`,而是指向原数组数据的某段偏移内存。
在 NumPy 中,数组的“逻辑视图”与“物理内存”是分离设计的。当你执行 b = a[1] 时,NumPy 并未复制数据,而是创建了一个新数组对象,它共享原始数据缓冲区(b.base is a),但拥有独立的元数据来描述如何访问其中一部分内容。
关键在于:
- b.data 是一个 memoryview 对象,指向原始扁平数据中第 4 个元素(即 a[1,0] = 4)所在的内存地址;
- b.shape = (3,) 表示该视图按一维解释;
- b.strides = (8,) 表示沿唯一维度每步跨 8 字节(即一个 int64 的大小),对应 [4,5,6] 的连续存储;
- 而 b.base 仅用于追溯源头,并不参与索引计算——真正决定“从哪开始读”的是 b.data 的起始地址。
可通过以下代码验证偏移关系:
import numpy as np
a = np.arange(1, 7, dtype=np.int64).reshape(2, 3) # 显式指定 int64,确保 stride=8
b = a[1]
# 查看原始数据起始地址(字节)
a_ptr = a.__array_interface__['data'][0]
b_ptr = b.__array_interface__['data'][0]
print(f"a data address: {a_ptr:#x}")
print(f"b data address: {b_ptr:#x}")
print(f"offset in bytes: {b_ptr - a_ptr}") # 应输出 24(即 3 * 8),对应跳过第一行 3 个 int64⚠️ 注意事项:
- strides 和 shape 始终作用于 data 所指的内存块,而非 base;
- 修改 b 会直接影响 a(因共享内存),这是视图的本质;
- 若需独立副本,请显式调用 .copy();
- data 属性不可直接赋值,但可通过 np.asarray(..., order='C') 等操作间接影响其底层内存布局。
总结来说,NumPy 不依赖隐藏的“offset”字段,而是将偏移信息编码在 data 的内存地址中,再配合 strides 和 shape 构成完整的内存访问协议——这正是其高效实现切片、转置、广播等操作的底层基础。









