从numpy函数返回子矩阵的引用
>>> a = np.arange(9).reshape((3, 3))
>>> a
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> def sub(a):
... return a[:2, :2]
...
>>> sub(a)
array([[0, 1],
[3, 4]])
>>> sub(a) = np.arange(4).reshape((2, 2))
File "<stdin>", line 1
SyntaxError: cant assign to function call
>>> t = a[:2, :2]
>>> t = np.arange(4).reshape((2, 2))
>>> a
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> a[:2, :2] = np.arange(4).reshape((2, 2))
>>> a
array([[0, 1, 2],
[2, 3, 5],
[6, 7, 8]])
这其实很简单,为什么会这样:当我输入 t = ..
的时候,我只是把 t
重新连接到了内存中的其他数据上。但是问题是:
- 我怎么才能搞定这个,把子矩阵的引用从函数里传出来?
- 同时还能修改这个子矩阵的值吗?
2 个回答
在 Python
中,正确的做法是:
In [97]: t=sub(a)
In [98]: t
Out[98]:
array([[0, 1],
[3, 4]])
In [100]: t[:]=np.arange(4).reshape(2,2)
In [101]: t
Out[101]:
array([[0, 1],
[2, 3]])
In [102]: a
Out[102]:
array([[0, 1, 2],
[2, 3, 5],
[6, 7, 8]])
这个函数返回的是原始数组的一个“视图”,就像在函数外面写 t = a[:2, :2]
一样。这里的 t=...
是在重新给变量赋值,而 t[:]=...
则是修改这个视图里的内容(数组元素)。因为这是一个视图,所以原始数组的内容也会跟着改变。这和你写的 a[:2, :2] =...
是一样的。
只要你使用 [:]
,就不需要中间的变量赋值:
In [104]: sub(a)[:]=np.zeros((2,2))
In [105]: a
Out[105]:
array([[0, 0, 2],
[0, 0, 5],
[6, 7, 8]])
- 我该如何将子矩阵的引用从函数中传递出来?
简单来说,在Python中你不能像在C++中那样从函数调用返回一个左值。在你的例子中,Python认为你是在直接给sub(a)
这个函数调用赋值,而不是给它返回的对象赋值。
当然,你可以使用索引来获取原始对象的引用,后面会详细说明。这将允许你修改原始矩阵的一部分。
- 这样还能改变这个子矩阵的值吗?
你可以在函数内部直接改变数组的子矩阵的值,像这样:
def sub(a):
a[:2, :2] = np.arange(4).reshape((2,2))
return a[:2, :2]
这样不仅会返回
修改后的子矩阵,还会改变原始数组本身。
对象是通过引用传递的,但它们的引用是通过值传递的:
就像Java一样,Python是通过值传递的,所以所有对象都是作为引用传递到函数中的,而这些引用是通过值传递的。
所以当你在函数内部索引这个数组对象并修改它的值时,你实际上是在修改这个引用指向的内存位置的值,但如果你改变引用本身,那么它不会修改原始对象,因为引用只是通过值传递的。
使用索引通过值传递对象的引用:
根据这个解释,你甚至可以进一步返回对象的引用,并在函数外部使用它来修改矩阵:
- 调用函数
sub(a)
,它将返回子矩阵的引用,这个引用本身是原始矩阵的引用,通过值传递。 - 将这个函数调用的结果赋值给另一个变量,这样就将子矩阵的引用通过值传递给这个新变量,
x = sub(a)
- 索引这个子矩阵以选择它的所有内容并修改它们的值:
x[:] = np.ones((2,2))
这也会修改原始矩阵
a
,因为你已经修改了x
所指向的内存位置的值。>>> x = sub(a) >>> x[:] = np.ones((2,2)) >>> x array([[1, 1], [1, 1]]) >>> a array([[1, 1, 2], [1, 1, 5], [6, 7, 8]])
或者,作为一个快捷方式:
>>> sub(a)[:] = np.ones((2,2)) >>> a array([[1, 1, 2], [1, 1, 5], [6, 7, 8]])
改变引用并不会改变对象:
然而,现在如果你将变量
x
设置为np.ones((2,2))
,那么a
就不会改变,因为这样做是改变了引用本身,而这个引用是通过值传递的。>>> x = 2 # this won't change a because x is a reference passed by value >>> a array([[1, 1, 2], [1, 1, 5], [6, 7, 8]])