拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Numpy阵列索引:查看或复制-取决于范围?

Numpy阵列索引:查看或复制-取决于范围?

白鹭 - 2022-02-14 2120 0 0

考虑以下阵列操作:

import numpy as np
def f(x):
     x  = 1
x = np.zeros(1)
f(x)       # changes `x`
f(x[0])    # doesn't change `x`
x[0]  = 1  # changes `x`

为什么x[0]根据 = 1发生在函式内部或外部而表现不同f

我可以将阵列的一部分传递给函式,以便函式修改原始阵列吗?

uj5u.com热心网友回复:

您甚至不需要函式呼叫来查看这种差异。

x 是一个阵列:

In [138]: type(x)
Out[138]: numpy.ndarray

索引阵列的元素会回传一个np.float64物件。它实际上从阵列中“取出”了值;它不是对阵列元素的参考。

In [140]: y=x[0]
In [141]: type(y)
Out[141]: numpy.float64

y很像 python 浮点数;你可以 =用同样的方式:

In [142]: y  = 1
In [143]: y
Out[143]: 1.0

但这不会改变x

In [144]: x
Out[144]: array([0.])

但这确实改变了x

In [145]: x[0]  = 1
In [146]: x
Out[146]: array([1.])

y=x[0]做了x.__getitem__电话。 x[0]=3做了x.__setitem__电话。 =使用__iadd__,但效果相似。

另一个例子:

改变x

In [149]: x[0] = 3
In [150]: x
Out[150]: array([3.])

但试图做同样的y失败:

In [151]: y[()] = 3
Traceback (most recent call last):
  File "<ipython-input-151-153d89268cbc>", line 1, in <module>
    y[()] = 3
TypeError: 'numpy.float64' object does not support item assignment

y[()]被允许。

basic使用切片对阵列进行索引确实会产生view可以修改的 a:

In [154]: x = np.zeros(5)
In [155]: x
Out[155]: array([0., 0., 0., 0., 0.])
In [156]: y= x[0:2]
In [157]: type(y)
Out[157]: numpy.ndarray
In [158]: y  = 1
In [159]: y
Out[159]: array([1., 1.])
In [160]: x
Out[160]: array([1., 1., 0., 0., 0.])

uj5u.com热心网友回复:

问题不在于范围,因为唯一取决于范围的是可用名称。所有物件都可以在任何具有名称的范围内访问。问题是可变性与不变性之一,以及了解运算子的作用。

x是一个可变的 numpy 阵列。直接在上面f运行x = 1 =是呼叫就地加法的运算子。换句话说,它确实*请注意对 的重新分配,这发生在函式中。这是就地运算子的一个特性,允许它们对不可变物件进行操作。在这种情况下,是一个真正的就地运算子,它只回传,一切都按预期作业。x = x.__iadd__(1)xndarray.__iadd__x

现在让我们f(x[0])以同样的方式分析x[0]呼叫* . 当您传入一个标量索引时,numpy 会提取一个单元素阵列并有效地呼叫它。结果是一个 python (或者,甚至可能是 a ,取决于你的阵列什么)。无论哪种方式,物件都是不可变的。一旦它被提取的,将在运营商代替名字的新物件,但变化不会看到函式外,阵列中的要少得多。在这种情况下,没有对 的参考,因此预计不会发生任何变化。x.__getitem__(0)int.item()intfloattupledtype__getitem__ =fx ffx

的示例x[0] = 1与呼叫f(x[0]). 它相当于呼叫*对 的呼叫只是 with 的部分,它回传一个新物件,但从不重新分配关键是python中的( ) 是与( ) 和(assingment) 分开的完全不同的运算子x.__setitem__(0, x.__getitem__(0).__iadd__(1))ftype(x).__getitem__(0).__iadd__(1)__setitem__[] =__setitem__[]__getitem__=

要使第二个示例 ( f(x[0]) 起作用,您必须传入一个可变物件。整数物件提取单个 python 物件,阵列索引进行复制。但是,切片索引回传一个可变的视图,并且系结到原始数??组存储器。因此,你可以做

f(x[0:1])  # changes `x`

在这种情况下f执行以下操作:x.__getitem__(slice(0, 1, None)).__iadd__(1)关键是__getitem__将可变视图回传到原始阵列中,而不是不可变的int

要了解为什么不仅物件是可变的而且它是原始阵列的视图很重要,请尝试f(x[[0]]). 用串列索引会生成一个阵列,但会生成一个副本。Inx[[0]].__iadd__将修改您就地传入的串列,但该串列不会复制回原始串列,因此更改不会传播。


*这是一个近似值。当被运算子呼叫时,dunder 方法实际上被称为 as type(x).__operator__(x, ...),而不是x.__operator__(...)

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *