NumPy:为什么需要明确复制一个值?

cqc*_*991 7 python numpy

arr = np.arange(0,11)
slice_of_arr = arr[0:6]
slice_of_arr[:]=99

# slice_of_arr returns
array([99, 99, 99, 99, 99, 99])
# arr returns
array([99, 99, 99, 99, 99, 99,  6,  7,  8,  9, 10])
Run Code Online (Sandbox Code Playgroud)

如上所示,您不能直接更改值slice_of_arr,因为它是一个视图arr,而不是一个新变量.

我的问题是:

  1. NumPy为什么这样设计?每次你需要.copy然后分配价值不是很乏味吗?
  2. 有什么我可以做的,摆脱.copy?如何更改NumPy的默认行为?

hpa*_*ulj 1

(numpy) __array_wrap__ 有什么作用?

谈论ndarray子类和钩子,例如__array_wrap__. np.array采用copy参数,强制结果为副本,即使其他考虑因素不需要它。 ravel返回一个视图,flatten一个副本。ndarray因此,构造一个强制复制的子类可能是可能的,而且可能不太困难。它可能涉及修改像__array_wrap__.

或者也许修改一下.__getitem__方法。索引涉及slice_of_arr = arr[0:6]对 的调用__getitem__。因为ndarray这是已编译的,但对于屏蔽数组,您可以使用 python 代码作为示例:

/usr/lib/python3/dist-packages/numpy/ma/core.py
Run Code Online (Sandbox Code Playgroud)

这可能很简单

def __getitem__(self, indx):
    """x.__getitem__(y) <==> x[y]
    """
    # _data = ndarray.view(self, ndarray) # change to:
    _data = ndarray.copy(self, ndarray)
    dout = ndarray.__getitem__(_data, indx)
    return dout
Run Code Online (Sandbox Code Playgroud)

但我怀疑,当您开发并完全测试这样的子类时,您可能会爱上默认的无复制方法。虽然这种“查看-v-复制”业务困扰了许多新用户(尤其是来自 MATLAB 的用户),但我还没有看到有经验的用户对此提出抱怨。查看其他 numpy SO 问题;你不会看到很多copy()电话。

即使是普通的 Python 用户也会问自己引用或切片是否是副本,以及某些内容是否可变。

例如列表:

In [754]: ll=[1,2,[3,4,5],6]
In [755]: llslice=ll[1:-1]
In [756]: llslice[1][1:2]=[10,11,12]
In [757]: ll
Out[757]: [1, 2, [3, 10, 11, 12, 5], 6]
Run Code Online (Sandbox Code Playgroud)

修改项目切片内的项目会修改原始列表中的同一项目。与 相比numpy,列表切片是一个副本。但这是一个浅拷贝。您必须付出额外的努力才能进行深复制 ( import copy)。

/usr/lib/python3/dist-packages/numpy/lib/index_tricks.py包含一些索引功能,旨在使某些索引操作更加方便。有几个实际上是具有自定义方法的类或类实例__getitem__。它们还可以作为如何自定义切片和索引的模型。