NumPy 副本与视图

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

就会发现数组 ab 的数据共享同一个内存。

副本与深拷贝

要返回 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,说明这里确实返回副本。

Previous