17、NumPy——副本和视图

副本是一个数据的完整的拷贝,如果我们对副本进行修改,它不会影响到原始数据,物理内存不在同一位置。

视图是数据的一个别称或引用,通过该别称或引用亦便可访问、操作原有数据,但原有数据不会产生拷贝。如果我们对视图进行修改,它会影响到原始数据,物理内存在同一位置。

视图一般发生在:

  • 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]

 

posted @ 2019-09-26 16:35  小新和风间  阅读(212)  评论(0编辑  收藏  举报