副本是一个数据的完整的拷贝,如果我们对副本进行修改,它不会影响到原始数据,物理内存不在同一位置。
视图是数据的一个别称或引用,通过该别称或引用亦便可访问、操作原有数据,但原有数据不会产生拷贝。如果我们对视图进行修改,它会影响到原始数据,物理内存在同一位置。
视图一般发生在:
- 1、numpy 的切片操作返回原数据的视图。
- 2、调用 ndarray 的 view() 函数产生一个视图。
副本一般发生在:
- Python 序列的切片操作,调用deepCopy()函数。
- 调用 ndarray 的 copy() 函数产生一个副本。
1、无复制
简单的赋值不会创建数组对象的副本。 相反,它使用原始数组的相同id()来访问它。 id()返回 Python 对象的通用标识符,类似于 C 中的指针。
此外,一个数组的任何变化都反映在另一个数组上。 例如,一个数组的形状改变也会改变另一个数组的形状。
1 import numpy as np 2 a = np.arange(6) 3 print('我们的数组是:') 4 print(a) 5 print('调用 id() 函数:') 6 print(id(a)) 7 print('a 赋值给 b:') 8 b = a 9 print(b) 10 print('b 拥有相同 id():') 11 print(id(b)) 12 print('修改 b 的形状:') 13 b.shape = (3, 2) 14 print(b) 15 print('a 的形状也修改了:') 16 print(a)
执行结果:
我们的数组是: [0 1 2 3 4 5] 调用 id() 函数: 1463892179824 a 赋值给 b: [0 1 2 3 4 5] b 拥有相同 id(): 1463892179824 修改 b 的形状: [[0 1] [2 3] [4 5]] a 的形状也修改了: [[0 1] [2 3] [4 5]]
2、视图或浅拷贝
ndarray.view() 方会创建一个新的数组对象,该方法创建的新数组的维数更改不会更改原始数据的维数。
1 import numpy as np 2 3 # 最开始 a 是个 3X2 的数组 4 a = np.arange(6).reshape(3, 2) 5 print('数组 a:') 6 print(a) 7 print('创建 a 的视图:') 8 b = a.view() 9 print(b) 10 print('两个数组的 id() 不同:') 11 print('a 的 id():') 12 print(id(a)) 13 print('b 的 id():') 14 print(id(b)) 15 # 修改 b 的形状,并不会修改 a 16 b.shape = (2, 3) 17 print('b 的形状:') 18 print(b) 19 print('a 的形状:') 20 print(a)
执行结果:
数组 a: [[0 1] [2 3] [4 5]] 创建 a 的视图: [[0 1] [2 3] [4 5]] 两个数组的 id() 不同: a 的 id(): 2213940021408 b 的 id(): 2213969165728 b 的形状: [[0 1 2] [3 4 5]] a 的形状: [[0 1] [2 3] [4 5]]
3、使用切片创建视图修改数据会影响到原始数组:
1 import numpy as np 2 arr = np.arange(12) 3 print('我们的数组:') 4 print(arr) 5 print('创建切片:') 6 a = arr[3:] 7 b = arr[3:] 8 a[1] = 123 9 b[2] = 234 10 print(arr) 11 print(id(a), id(b), id(arr[3:]))
执行结果:
我们的数组: [ 0 1 2 3 4 5 6 7 8 9 10 11] 创建切片: [ 0 1 2 3 123 234 6 7 8 9 10
变量 a,b 都是 arr 的一部分视图,对视图的修改会直接反映到原数据中。但是我们观察 a,b 的 id,他们是不同的,也就是说,视图虽然指向原数据,但是他们和赋值引用还是有区别的。
4、副本或深拷贝
ndarray.copy() 函数创建一个副本。 对副本数据进行修改,不会影响到原始数据,它们物理内存不在同一位置。
1 import numpy as np 2 a = np.array([[10, 10], [2, 3], [4, 5]]) 3 print('数组 a:') 4 print(a) 5 print('创建 a 的深层副本:') 6 b = a.copy() 7 print('数组 b:') 8 print(b) 9 # b 与 a 不共享任何内容 10 print('我们能够写入 b 来写入 a 吗?') 11 print(b is a) 12 print('修改 b 的内容:') 13 b[0, 0] = 100 14 print('修改后的数组 b:') 15 print(b) 16 print('a 保持不变:') 17 print(a)
执行结果:
数组 a: [[10 10] [ 2 3] [ 4 5]] 创建 a 的深层副本: 数组 b: [[10 10] [ 2 3] [ 4 5]] 我们能够写入 b 来写入 a 吗? False 修改 b 的内容: 修改后的数组 b: [[100 10] [ 2 3] [ 4 5]] a 保持不变: [[10 10] [ 2 3] [ 4 5]]
拓展:Python 中 list 的拷贝与 numpy 的 array 的拷贝
1.python中列表list的拷贝,会有什么需要注意的呢?
Python 变量名相当于标签名。
list2=list1 直接赋值,实质上指向的是同一个内存值。任意一个变量 list1(或list2)发生改变,都会影响另一个 list2(或list1)。
list1 = [i for i in range(4)] list2 = list1 print('原列表为:\n', list1) print('修改list2:') list2[0] = 100 print('list1={}\nlist2={}'.format(list1, list2))
执行结果:
原列表为: [0, 1, 2, 3] 修改list2: list1=[100, 1, 2, 3] list2=[100, 1, 2, 3]