赞
踩
在介绍Numpy中的切片前首先要引入三个概念:引用、副本和视图
引用即通过赋值操作使得不同标识符指向同一个对象。如果该对象为可变对象,对一个变量进行了修改,会同步修改。
副本是一个数据的完整的拷贝,如果我们对副本进行修改,它不会影响到原始数据,物理内存不在同一位置。
视图是数据的一个别称或引用,通过该别称或引用亦便可访问、操作原有数据,但原有数据不会产生拷贝。如果我们对视图进行修改,它会影响到原始数据。
引用一般发生在:
视图一般发生在:
副本一般发生在:
对应python中的概念:numpy中引用 = python中引用;numpy中视图 = python中浅拷贝;numpy中副本 = python中深拷贝
浅拷贝与深拷贝之间的区别仅当传入的对象为复合对象时(即传入的对象包含其他的对象,如包含列表等)会有所差异:
# 要导入copy库
import copy
a = [[1,2,3],4]
# 浅拷贝
b = copy.copy(a)
print(id(a),id(b),id(a[0]),id(b[0]))
# 深拷贝
c = copy.deepcopy(a)
print(id(a),id(c),id(a[0]),id(c[0]))
2890993692864 2890993695808 2890992900288 2890992900288
2890993692864 2890992901248 2890992900288 2890871413248
可以通过切片操作返回原数据的视图;还可以调用 ndarray.view() 函数产生一个视图。
numpy中的视图 等价于 python中的浅拷贝
返回的一个新的数组对象(id不同)
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
arr1 = arr[:2]
print(id(arr),id(arr1),id(arr[0]),id(arr1[0]))
arr1[0][0] = 10
arr1.shape = (3,2)
print(arr,'\n',arr1)
2890993705584 2890993705680 2890993707024 2890993707024
[[10 2 3]
[ 4 5 6]
[ 7 8 9]]
[[10 2]
[ 3 4]
[ 5 6]]
# 通过view()方法创建视图
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
arr1 = arr.view()
print(id(arr),id(arr1),id(arr[0]),id(arr1[0]))
2890993564432 2890993705200 2890993675696 2890993675696
输出结果和想象的一致,但是这其中其实是有一个小坑的,那就是id(arr[0]),id(arr1[0])
和上面拷贝列表时的id(a[0]),id(b[0])
是两个完全不同的概念,不信你看下面的创建副本或深拷贝的id(arr[0]),id(arr1[0]
可以通过copy.deepcopy()创建副本;也可以通过ndarray.copy()创建副本
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
arr1 = arr.copy()
id(arr),id(arr1),id(arr[0]),id(arr1[0])
(2890993675216, 2890993707792, 2890993564432, 2890993564432)
import copy
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
arr1 = copy.deepcopy(arr)
id(arr),id(arr1),id(arr[0]),id(arr1[0])
(1567028693200, 1567028540976, 1567028540304, 1567028540304)
你会发现id(arr[0]),id(arr1[0])
竟然是同一个id,下面我将解释这背后的原理:
It must be noted that the returned array is a view, i.e., it is not a copy of the original, but points to the same values in memory as does the original array.
id(arr[0])
执行结束之后,这个对象就被释放了,但是这块内存并没有被释放,当执行id(arr1[0])
又临时创建了一个ndarray对象,只不过它用的也是同一个内存,没有申请新的内存,这会让你以为arr[0]
与arr1[0]
是同一个对象。使用is
你就会发现这两个是不同的对象,它们的生命期不重叠id():返回对象的“标识值”。该值是一个整数,在此对象的生命周期中保证是唯一且恒定的。两个生命期不重叠的对象可能具有相同的 id() 值。
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
arr1 = arr.copy()
arr[0] is arr1[0]
False
而我们把arr[0]
和arr1[0]
通过变量进行引用,而不是作为临时对象,强行令他们的生存周期有重叠,然后你会发现他们作为不同对象,其地址确实是不一样的。
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
arr1 = arr.copy()
a = arr[0]
b = arr1[0]
id(a),id(b)
(1567028529552, 1567028423824)
下面这个例子,能更好的理解python的内容管理机制:
arr[0]
,用a保存该临时对象的id后 --》释放该临时对象arr[0]
,使用了上一块释放了临时对象的内存,并且将该对象用变量c保存了下来,此时该对象不会被释放arr1[0]
,由于上一部分对象没有被释放,因此需要重新申请一块新的内存,用b保存该临时对象的id后 --》释放该临时对象arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
arr1 = arr.copy()
a = id(arr[0])
c = arr[0]
b = id(arr1[0])
print(a,id(c),b)
1567028527536 1567028527536 1567028691856
参考:
https://www.zhihu.com/question/275830564
https://www.bilibili.com/video/BV1yY4y1t77a?spm_id_from=333.999.0.0
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。