【Python】Numpy 与 标准python中的=,视图(浅拷贝),深拷贝
一,= (完全不复制)
1,NumPy
In [2]: a = np.arange(8)
In [3]: a
Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7])
In [4]: id(a)
Out[4]: 140297255040544
In [5]: b = a
In [6]: b
Out[6]: array([0, 1, 2, 3, 4, 5, 6, 7])
In [7]: id(b)
Out[7]: 140297255040544
In [8]: b[0] = -1
In [9]: a # a和b是同一个ndarray对象的两个名字,对b的任何操作都会影响a
Out[9]: array([-1, 1, 2, 3, 4, 5, 6, 7])
In [17]: b.shape = 2,4
In [18]: b
Out[18]:
array([[-1, 1, 2, 3],
[ 4, 5, 6, 7]])
In [19]: a # 也会改变a的形状
Out[19]:
array([[-1, 1, 2, 3],
[ 4, 5, 6, 7]])
(1)不创建新的数组,同个内存地址。
(2)新旧数组是用一个对象的不同标签(名字)而已。
(3)改变其中一个,另一个也会跟着变化,包括改变数组中的数据,以及数组的形状。
2,标准python
赋值本质上 是对象之间的引用传递而已。也就是多个对象指向同一个数据空间。
In [31]: a
Out[31]: [1, 2, 3, 4, [-1, -2]]
In [32]: b = a
In [33]: id(a)
Out[33]: 140297286164616
In [34]: id(b)
Out[34]: 140297286164616
In [35]: b[0] = -1
In [36]: b[4].append(-3) #嵌套的列表也是相同地址,修改后也会影响到a
In [37]: b
Out[37]: [-1, 2, 3, 4, [-1, -2, -3]]
In [38]: a
Out[38]: [-1, 2, 3, 4, [-1, -2, -3]]
二,视图(浅拷贝)
1,numpy中的视图
In [39]: a = np.arange(8)
In [40]: b = a[:] #切分的结果为原数组的视图,非复制
In [41]: b
Out[41]: array([0, 1, 2, 3, 4, 5, 6, 7])
In [42]: a
Out[42]: array([0, 1, 2, 3, 4, 5, 6, 7])
In [43]: id(a)
Out[43]: 140297254632064
In [44]: id(b)
Out[44]: 140297254522352 # a,b的内存地址不同
In [45]: a is b
Out[45]: False
In [46]: b[0] = -1
In [47]: b
Out[47]: array([-1, 1, 2, 3, 4, 5, 6, 7])
In [48]: a
Out[48]: array([-1, 1, 2, 3, 4, 5, 6, 7]) #修改b 也会作用于a
In [49]: np.may_share_memory(a, b)
Out[49]: True # a,b共享内存
In [50]: a.flags.owndata
Out[50]: True # a 拥有数据
In [51]: b.flags.owndata
Out[51]: False # b 不拥有数据
In [52]: b.base is a
Out[52]: True # b 基于 a
In [53]: b.shape = 4,2
In [54]: b
Out[54]:
array([[-1, 1],
[ 2, 3],
[ 4, 5],
[ 6, 7]])
In [55]: a
Out[55]: array([-1, 1, 2, 3, 4, 5, 6, 7]) # 数据形状的改变不影响
(1)不同的两个对象,可以理解为两个存储了指向同一个内存块的指针(np.may_share_memory(a, b) 为 True)。
(2)改变一个的形状不会改变另一个,只是数据共享。
(3)改变一个数据会同时改变另一个:数据共享,上面的demo中,b = a[:]会创建一个新的对象b(所以说 id 和a不一样),但是b的数据完全来自于a,和a保持完全一致(b.base is a 为 True),换句话说,b的数据完全由a保管(a.flags.owndata 为 True),他们两个的数据变化是一致的。
(4)可以理解为数据库中的视图与表的关系,视图不存储数据,表中存储数据,通过视图可以修改表中的数据。
2,标准python 浅拷贝
In [56]: a = [1,2,[-1,-2]]
In [57]: b = a[:] #等价于b = a.copy()
In [58]: id(a)
Out[58]: 140297111902920
In [59]: id(b)
Out[59]: 140297254461960 # a,b为不同对象
In [60]: [id(x) for x in a]
Out[60]: [9330720, 9330752, 140297125092744]
In [61]: [id(x) for x in b]
Out[61]: [9330720, 9330752, 140297125092744] #第一层元素的地址不同,但是深层原始相同地址
In [62]: b[0] = 0
In [63]: b
Out[63]: [0, 2, [-1, -2]]
In [64]: a
Out[64]: [1, 2, [-1, -2]]
In [65]: b[2].append(-3)
In [66]: b
Out[66]: [0, 2, [-1, -2, -3]]
In [67]: a
Out[67]: [1, 2, [-1, -2, -3]]
浅拷贝可以理解为藕断丝连,浅拷贝只拷贝第一层数据,既节省了内存空间和拷贝时间,速度更快,但存在深层数据还是同个地址,修改会受到影响。
当我们使用下面的操作的时候,会产生浅拷贝的效果:
使用切片[:]操作 b = a[:]
使用工厂函数(如list/dir/set) b = list(a)
使用copy模块中的copy()函数 b = a.cppy ,需要导入copy
三,深拷贝
1,numpy 深拷贝
In [95]: a = np.arange(8)
In [96]: b = a.copy()
In [97]: a is b
Out[97]: False
In [98]: b.base is a
Out[98]: False
In [99]: a.flags.owndata
Out[99]: True
In [100]: b.flags.owndata
Out[100]: True
In [101]: np.may_share_memory(a,b)
Out[101]: False
深拷贝后,两个数组相互独立,拥有各自的数据,不在共享内存数据。修改形状和数据都互不影响。
2,标准python 深拷贝
In [72]: import copy
In [83]: a
Out[83]: [1, 2, [-1, -2, -3]]
In [84]: b = copy.deepcopy(a)
In [85]: [id(x) for x in a]
Out[85]: [9330720, 9330752, 140297125092744]
In [86]: [id(x) for x in b]
Out[86]: [9330720, 9330752, 140297111147976] # 不可变对象id值一样,可变对象id值不一样,
In [87]: b[1] = 0
In [88]: b[2][1] = 0
In [89]: [id(x) for x in a]
Out[89]: [9330720, 9330752, 140297125092744]
In [90]: [id(x) for x in b]
Out[90]: [9330720, 9330688, 140297111147976] #不可变对象只要值改变就会指向新地址,不会影响与其值改变
参考
https://www.cnblogs.com/eczhou/p/7860668.html
https://blog.csdn.net/weixin_43965642/article/details/85770177