B18_NumPy副本和视图(视图或浅拷贝、副本或深拷贝、深浅拷贝)

NumPy副本和视图

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

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

视图一般发生在:

  • 1、numpy的切片操作返回原数据的视图。

  • 2、调用ndarray的view()函数产生一个视图。
    副本一般发生在:

  • Python序列的切片操作,调用deepCopy()函数。

  • 调用ndarray的copy()函数产生一个副本。
    无复制
    简单的赋值不会创建数组对象的副本。 相反,它使用原始数组的相同id()来访问它。 id()返回 Python 对象的通用标识符,类似于 C 中的指针。

此外,一个数组的任何变化都反映在另一个数组上。 例如,一个数组的形状改变也会改变另一个数组的形状。
实例

import numpy as np

a = np.arange(6)
print ('我们的数组是:')
print (a)
print ('调用 id() 函数:')
print (id(a))
print ('a 赋值给 b:')
b = a
print (b)
print ('b 拥有相同 id():')
print (id(b))
print ('修改 b 的形状:')
b.shape =  3,2  
print (b)
print ('a 的形状也修改了:')
print (a)

输出结果为:

我们的数组是:
[0 1 2 3 4 5]
调用 id() 函数:
2160389265488
a 赋值给 b:
[0 1 2 3 4 5]
b 拥有相同 id()2160389265488
修改 b 的形状:
[[0 1]
 [2 3]
 [4 5]]

视图或浅拷贝

ndarray.view() 方会创建一个新的数组对象,该方法创建的新数组的维数更改不会更改原始数据的维数。
实例

import numpy as np

# 最开始 a 是个 3X2 的数组
a = np.arange(6).reshape(3, 2)
print('数组 a:')
print(a)
print('创建 a 的视图:')
b = a.view()
print(b)
print('两个数组的 id() 不同:')
print('a 的 id():')
print(id(a))
print('b 的 id():')
print(id(b))
# 修改 b 的形状,并不会修改 a
b.shape = 2, 3
print('b 的形状:')
print(b)
print('a 的形状:')
print(a)

输出结果为:

数组 a:
[[0 1]
 [2 3]
 [4 5]]
创建 a 的视图:
[[0 1]
 [2 3]
 [4 5]]
两个数组的 id() 不同:
a 的 id()2336918390944
b 的 id()2336918391184
b 的形状:
[[0 1 2]
 [3 4 5]]
a 的形状:
[[0 1]
 [2 3]
 [4 5]]

使用切片创建视图修改数据会影响到原始数组:

import numpy as np

arr = np.arange(12)
print('我们的数组:')
print(arr)
print('创建切片:')
a = arr[3:]
b = arr[3:]
a[1] = 123
b[2] = 234
print(arr)
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  11]
2897481484528 2897481484448 2897481484688

变量 a,b 都是 arr 的一部分视图,对视图的修改会直接反映到原数据中。但是我们观察 a,b 的 id,他们是不同的,也就是说,视图虽然指向原数据,但是他们和赋值引用还是有区别的。

副本或深拷贝

ndarray.copy() 函数创建一个副本。 对副本数据进行修改,不会影响到原始数据,它们物理内存不在同一位置。

import numpy as np

a = np.array([[10, 10], [2, 3], [4, 5]])
print('数组 a:')
print(a)
print('创建 a 的深层副本:')
b = a.copy()
print('数组 b:')
print(b)
# b 与 a 不共享任何内容
print('我们能够写入 b 来写入 a 吗?')
print(b is a)
print('修改 b 的内容:')
b[0, 0] = 100
print('修改后的数组 b:')
print(b)
print('a 保持不变:')
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 中,对象赋值实际上是对象的引用。当创建一个对象,然后把它赋给另一个变量的时候,Python 并没有拷贝这个对象,而只是拷贝了这个对象的引用,我们称之为浅拷贝。

在 Python 中,为了使当进行赋值操作时,两个变量互补影响,可以使用 copy 模块中的 deepcopy 方法,称之为深拷贝。

append()函数

当 list 类型的对象进行 append 操作时,实际上追加的是该对象的引用。
id() 函数:返回对象的唯一标识,可以类比成该对象在内存中的地址。

>>>alist = []
>>> num = [2]
>>> alist.append( num )
>>> id( num ) == id( alist[0] )
True

如上例所示,当 num 发生变化时(前提是 id(num) 不发生变化),alist 的内容随之会发生变化。往往会带来意想不到的后果,想避免这种情况,可以采用深拷贝解决:

alist.append( copy.deepcopy( num ) )
发布了1051 篇原创文章 · 获赞 342 · 访问量 393万+

猜你喜欢

转载自blog.csdn.net/toto1297488504/article/details/105061703
今日推荐