基本问题是:在做什么时会发生什么:a[i] += b
?
鉴于以下内容:
import numpy as np
a = np.arange(4)
i = a > 0
i
= array([False, True, True, True], dtype=bool)
我理解:
a[i] = x
与a.__setitem__(i, x)
相同,后者直接指定i
a += x
与a.__iadd__(x)
相同,后者会添加但是当我做时会发生什么:
a[i] += x
具体做法是:
a[i] = a[i] + x
相同吗? (这不是就地操作)i
是:int
,它会对这种情况产生影响吗?
ndarray
索引或slice
或a = np.zeros(4)
x = np.arange(4)
indices = np.zeros(4,dtype=np.int) # duplicate indices
a[indices] += x
a
= array([ 3., 0., 0., 0.])
对象背景
我开始深入研究这个问题的原因是我在使用重复索引时遇到了非直观的行为:
{{1}}
有关this question中重复索引的更多有趣内容。
答案 0 :(得分:11)
您需要意识到的第一件事是a += x
并不完全映射到a.__iadd__(x)
,而是映射到a = a.__iadd__(x)
。请注意,documentation明确指出就地运算符返回其结果,并且不必是self
(尽管实际上通常是这样)。这意味着a[i] += x
通常会映射到:
a.__setitem__(i, a.__getitem__(i).__iadd__(x))
因此,添加技术上就地发生,但仅限于临时对象。尽管如此,创建的临时对象仍然可能比调用__add__
少一个。
答案 1 :(得分:4)
实际上这与numpy无关。 python中没有“set / getitem in”,这些东西相当于a[indices] = a[indices] + x
。知道了,事情变得非常明显。 (编辑:正如lvc所写的那样,实际上右侧是合适的,因此如果这是合法的语法,那么它是a[indices] = (a[indices] += x)
,虽然具有相同的效果,但是这样做很好)
当然a += x
通过将a映射到np.add
out
参数来实际就位。
之前已经讨论过,numpy对此无能为力。虽然有一个想法让np.add.at(array, index_expression, x)
至少允许这样的操作。
答案 2 :(得分:2)
正如Ivc所解释的那样,没有就地项目添加方法,因此它使用__getitem__
,然后使用__iadd__
,然后使用__setitem__
。这是一种凭经验观察这种行为的方法:
import numpy
class A(numpy.ndarray):
def __getitem__(self, *args, **kwargs):
print "getitem"
return numpy.ndarray.__getitem__(self, *args, **kwargs)
def __setitem__(self, *args, **kwargs):
print "setitem"
return numpy.ndarray.__setitem__(self, *args, **kwargs)
def __iadd__(self, *args, **kwargs):
print "iadd"
return numpy.ndarray.__iadd__(self, *args, **kwargs)
a = A([1,2,3])
print "about to increment a[0]"
a[0] += 1
打印
about to increment a[0]
getitem
iadd
setitem
答案 3 :(得分:2)
我认为这里的主要区别是就地运算符可能会返回相同的引用,但NumPy的效果与Python不同。
string
这些是相同的参考。
>>> a = 1
>>> b = a
>>> a is b
True
就地添加会创建一个新参考。
>>> a += 4
>>> a
5
>>> b
1
同样这些是相同的参考,但是就地,运算符会有不同的效果。
>>> import numpy as np
>>> a = np.array([1, 2, 3], float)
>>> b = a
>>> a is b
True
添加ndarray更新引用。这与调用>>> a += 4
>>> a
array([ 5., 6., 7.])
>>> b
array([ 5., 6., 7.])
不同,后者在新引用中创建副本。
numpy.add
这里的危险是如果引用被传递到不同的范围。
>>> a = a + 4
>>> a
array([ 9., 10., 11.])
>>> b
array([ 5., 6., 7.])
>>> def f(x):
... x += 4
... return x
的参数引用被传递到x
的范围内,该范围不会复制,实际上会更改该引用的值并将其传回。
f
这可能会造成混淆,因此只能在属于当前作用域的引用上使用就地运算符,并注意借用的引用。