Python 使用高级索引的分配是否复制阵列数据?

Python 使用高级索引的分配是否复制阵列数据?,python,numpy,copy,Python,Numpy,Copy,我正在慢慢地试图理解numpy中的views和copys之间的区别,以及可变和不可变类型之间的区别 如果我访问数组的一部分,它应该返回一个副本。这似乎是真的: In [1]: import numpy as np In [2]: a = np.zeros((3,3)) In [3]: b = np.array(np.identity(3), dtype=bool) In [4]: c = a[b] In [5]: c[:] = 9 In [6]: a Out[6]: array([[ 0

我正在慢慢地试图理解numpy中的
view
s和
copy
s之间的区别,以及可变和不可变类型之间的区别

如果我访问数组的一部分,它应该返回一个副本。这似乎是真的:

In [1]: import numpy as np
In [2]: a = np.zeros((3,3))
In [3]: b = np.array(np.identity(3), dtype=bool)

In [4]: c = a[b]

In [5]: c[:] = 9

In [6]: a
Out[6]: 
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])
由于
c
只是一个副本,因此它不会共享数据,更改它也不会变异
a
。然而,这让我感到困惑:

In [7]: a[b] = 1

In [8]: a
Out[8]: 
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
所以,看起来,即使我使用高级索引,赋值仍然将左边的东西视为一个视图。显然,第2行中的
a
与第6行中的
a
是相同的对象/数据,因为变异
c
对其没有影响

所以我的问题是:第8行中的
a
是与之前相同的对象/数据(当然不包括对角线)还是副本?换句话说,
a
的数据是复制到新的
a
,还是其数据发生了突变

例如,它是否像:

x = [1,2,3]
x += [4]
或者说:

y = (1,2,3)
y += (4,)

我不知道如何检查这一点,因为无论哪种情况,
a.flags.owndata
都是
True
。如果我用一种令人困惑的方式思考这个问题,请随时详细说明或回答不同的问题。

是的,它是同一个对象。以下是您检查的方式:

>>> a
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])
>>> a2 = a
>>> a[b] = 1
>>> a2 is a
True
>>> a2
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
在Python中为某个表达式赋值与读取该表达式的值不同。如果执行
c=a[b]
,且在等号右侧有
a[b]
,则返回一个新对象。如果执行
a[b]=1
,并在等号左侧使用
a[b]
,则会修改原始对象


事实上,像
a[b]=1这样的表达式不能更改
a
绑定的名称。处理
obj[index]=value
的代码只知道对象
obj
,而不是用来引用该对象的名称,因此它不能更改该名称所引用的内容。

当您执行
c=a[b]
时,
a.\uu get\u item\uu
b
作为其唯一参数调用,无论返回什么,都将分配给
c

当您执行
a[b]=c
时,
a.\uuuu setitem\uuuuu
将使用
b
c
作为参数进行调用,返回的任何内容都将被默默地丢弃


因此,尽管有相同的
a[b]
语法,但这两个表达式所做的事情不同。您可以子类化
ndarray
,重载这两个函数,并使它们的行为不同。在numpy中,默认情况下,前者返回一个副本(如果
b
是一个数组),但后者修改
a

这似乎是常见的误解,引用了官方文件: ()

这里的经验法则是:在左值索引(即。 索引放置在赋值的左侧值中), 不创建阵列的视图或副本(因为不需要创建)。 但是,对于常规值,上述创建视图的规则不适用 申请


换句话说,,
view
copy
的概念仅指从
numpy
对象检索值的情况。

我认为值得在numpy文档中明确指出一个事实,即当用作左值时,即使是高级索引也会修改原始数组。请参阅视图与副本的精彩摘要。