我正在慢慢地尝试理解numpy中view
和copy
之间的区别,以及可变类型和不可变类型之间的区别。
如果我使用'advanced indexing'访问数组的一部分,则应该返回副本。这似乎是真的:
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
。如果我以一种令人困惑的方式考虑这个问题,请随时详细说明或回答不同的问题。
答案 0 :(得分:8)
执行c = a[b]
时,调用a.__get_item__
作为唯一参数b
,并将返回的内容分配给c
。
当您执行a[b] = c
时,a.__setitem__
作为参数调用b
和c
,并且会无声地丢弃返回的内容。
因此尽管具有相同的a[b]
语法,但两个表达式都在做不同的事情。您可以继承ndarray
,重载这两个函数,并使它们的行为不同。正如numpy中的默认情况一样,前者返回一个副本(如果b
是一个数组),但后者会修改a
。
答案 1 :(得分:4)
是的,它是同一个对象。以下是您检查的方式:
>>> 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
,而不是用于引用该对象的名称,因此它无法更改该名称所指的内容。
答案 2 :(得分:0)
这似乎是一种常见的误解,引用了官方文件: (https://scipy-cookbook.readthedocs.io/items/ViewsVsCopies.html)
这里的经验法则可以是:在左值索引的上下文中(即 索引放在作业的左侧值中), 没有创建数组的视图或副本(因为没有必要)。 但是,对于常规值,上述用于创建视图的规则会 申请。
换句话说,view
或copy
的概念仅是指从numpy
对象检索值的情况。