14

我正在慢慢尝试理解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.,  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.]])

所以,看起来,即使我使用高级索引,赋值仍然把左边的东西当作一个视图。显然第a2 行与第 6 行是相同的对象/数据a,因为变异c对其没有影响。

所以我的问题是:第a8 行中的对象/数据是否与以前相同(当然不包括对角线)还是副本?换句话说,是a的数据被复制到了新的a,还是它的数据在原地发生了变异?

例如,是不是这样:

x = [1,2,3]
x += [4]

或喜欢:

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

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

4

3 回答 3

10

当您这样做时c = a[b]a.__get_item__b作为其唯一参数调用,并且将返回的任何内容分配给c.

当您这样做时a[b] = ca.__setitem__会使用bandc作为参数调用,并且返回的任何内容都会被默默地丢弃。

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

于 2013-03-28T21:13:33.343 回答
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]在等号的左边,它会修改原始对象。

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

于 2013-03-28T20:53:04.293 回答
1

这似乎是常见的误解,引用官方文档:(https://scipy-cookbook.readthedocs.io/items/ViewsVsCopies.html

这里的经验法则可以是:在左值索引的上下文中(即索引放置在赋值的左侧值中),不创建数组的视图或副本(因为没有必要)。但是,对于常规值,上述创建视图的规则确实适用。

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

于 2019-02-23T01:15:13.480 回答