考虑以下阵列操作:
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)
x
ndarray.__iadd__
x
现在让我们f(x[0])
以同样的方式分析。x[0]
呼叫* . 当您传入一个标量索引时,numpy 会提取一个单元素阵列并有效地呼叫它。结果是一个 python (或者,甚至可能是 a ,取决于你的阵列是什么)。无论哪种方式,物件都是不可变的。一旦它被提取的,将在运营商代替名字中的新物件,但变化不会看到函式外,阵列中的要少得多。在这种情况下,没有对 的参考,因此预计不会发生任何变化。x.__getitem__(0)
int
.item()
int
float
tuple
dtype
__getitem__
=
f
x
f
f
x
的示例x[0] = 1
与呼叫f(x[0])
. 它相当于呼叫*。对 的呼叫只是 with 的部分,它回传一个新物件,但从不重新分配。关键是python中的( ) 是与( ) 和(assingment) 分开的完全不同的运算子。x.__setitem__(0, x.__getitem__(0).__iadd__(1))
f
type(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 评论