NumPy中reshape后数组base属性疑问:为何D的base指向C而非None?
关于NumPy reshape后数组是副本还是视图的疑问
我想通过base属性判断NumPy执行reshape操作后数组是副本还是视图——预期副本的base为None,视图的base指向原数组。但运行以下代码后,结果和预期有出入:
import numpy A = numpy.array([[1,2,20],[3,4,40],[5,6,60],[7,8,80],[9,10,100]]) print('A:\n',A) print('A base:\n', A.base) print('A initial shape:', A.shape) B = A.reshape(3,5) print('B:\n', B) print('B base:\n', B.base) C = A[1:3,0:2] print('C:\n', C) print('C base:\n', C.base) D = C.reshape(4,1) print('D:\n', D) print('D base:\n', D.base)
得到输出:
A: [[ 1 2 20] [ 3 4 40] [ 5 6 60] [ 7 8 80] [ 9 10 100]] A base: None A initial shape: (5, 3) B: [[ 1 2 20 3 4] [ 40 5 6 60 7] [ 8 80 9 10 100]] B base: [[ 1 2 20] [ 3 4 40] [ 5 6 60] [ 7 8 80] [ 9 10 100]] C: [[3 4] [5 6]] C base: [[ 1 2 20] [ 3 4 40] [ 5 6 60] [ 7 8 80] [ 9 10 100]] D: [[3] [4] [5] [6]] D base: [[3 4] [5 6]]
我理解A是原始数组,base为None;B和C是A的视图,base指向A。但我不明白D的base属性:预期D是新数组(副本),base应为None,可它的base却指向C,而C本身是A的视图。为什么会这样?C难道是某个未定义数组的视图吗?为什么D不是base为None的数组?
解答
- 首先要明确:reshape操作在绝大多数情况下返回的都是原数组的视图,只有当原数组的数据存储不连续时,才会返回副本。
- 先看C的本质:C是A通过切片得到的视图,它的
base指向A,说明C并没有独立存储数据,只是对A中某块连续区域的引用。C本身的存储是连续的(你可以用C.flags.contiguous验证,结果是True),所以对C执行reshape时,完全不需要复制数据——只需要调整数组的形状描述(shape、strides等元数据),就能得到D。 - 为什么D的
base是C而不是A?因为base属性记录的是当前数组直接依赖的数组对象,而不是最底层的原始数组。D是基于C生成的视图,所以它直接指向C;而C的base是A,这意味着D最终还是共享A的数据。你可以测试修改D的元素,比如D[0] = 99,会发现C和A中对应位置的元素也跟着变,这就证明了它们共享同一份数据。 - 什么时候reshape会返回副本?如果原数组的存储不连续(比如对一个非连续的视图再做不规则切片),reshape无法通过调整元数据来实现形状变换,这时候才会创建副本,此时
base才会是None。
举个例子,如果先对C做一个非连续的切片:
E = C[:, ::2] # 取C的第0列,此时E的存储是非连续的 F = E.reshape(2,1) print(F.base) # 此时F的base是None,因为reshape时必须创建副本
总结一下:reshape优先返回视图,只要数据存储连续就能做到;base只记录直接依赖的数组,不是最底层的原始数组,所以D的base是C,而C的base是A,它们最终都共享A的数据。
内容的提问来源于stack exchange,提问作者volperossa
相关产品推荐
相关产品推荐

