import numpy as np
副本是一个数据的完整的拷贝,如果我们对副本进行修改,它不会影响到原始数据,物理内存不在同一位置。
视图是数据的一个别称或引用,通过该别称或引用亦便可访问、操作原有数据,但原有数据不会产生拷贝。如果我们对视图进行修改,它会影响到原始数据,物理内存在同一位置。
视图与浅拷贝
切片返回视图
在 NumPy 中,ndarray
数组的切片操作返回视图。
a = np.arange(6)
b = a[1:]
a, b
(array([0, 1, 2, 3, 4, 5]), array([1, 2, 3, 4, 5]))
现在我们来修改数组 b
:
b[0] = 0
a, b
(array([0, 0, 2, 3, 4, 5]), array([0, 2, 3, 4, 5]))
可以看到,a[1]
和 b[0]
的值都变为 0
了。
但是,要特别注意:在 Python 中,列表的切片返回副本!
a = list(range(6))
b = a[1:]
a, b
([0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5])
可以看到,这里 a[1]
的值并没有被修改为 0
,说明 Python 列表返回副本。
b[0] = 0
a, b
([0, 1, 2, 3, 4, 5], [0, 2, 3, 4, 5])
view 方法显式返回视图
ndarray.view
方法会创建一个新的数组对象,该方法创建的新数组的维数变化不会改变原始数据的维数。
a = np.arange(6)
b = a.view()
a, b
(array([0, 1, 2, 3, 4, 5]), array([0, 1, 2, 3, 4, 5]))
现在,我们修改 b
的形状:
b.shape = (2, 3)
a, b
(array([0, 1, 2, 3, 4, 5]),
array([[0, 1, 2],
[3, 4, 5]]))
可以发现 a
的形状并没有修改。
但是,如果我们修改数组元素的值:
a[1] = 0
a, b
(array([0, 0, 2, 3, 4, 5]),
array([[0, 0, 2],
[3, 4, 5]]))
就会发现数组 a
和 b
的数据共享同一个内存。
副本与深拷贝
要返回 ndarray
数组的副本,必须显式地调用 ndarray.copy
方法。
a = list(range(6))
b = a.copy()
a, b
([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5])
此时我们再修改 b[0]
的值:
b[0] = 0
a, b
([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5])
可以看到,这里 a[1]
的值并没有被修改为 0
,说明这里确实返回副本。