从numpy函数返回子矩阵的引用

5 投票
2 回答
1599 浏览
提问于 2025-04-21 05:26
>>> 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 重新连接到了内存中的其他数据上。但是问题是:

  1. 我怎么才能搞定这个,把子矩阵的引用从函数里传出来?
  2. 同时还能修改这个子矩阵的值吗?

2 个回答

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]])
1
  1. 我该如何将子矩阵的引用从函数中传递出来?

简单来说,在Python中你不能像在C++中那样从函数调用返回一个左值。在你的例子中,Python认为你是在直接给sub(a)这个函数调用赋值,而不是给它返回的对象赋值。

当然,你可以使用索引来获取原始对象的引用,后面会详细说明。这将允许你修改原始矩阵的一部分。

  1. 这样还能改变这个子矩阵的值吗?

你可以在函数内部直接改变数组的子矩阵的值,像这样:

def sub(a):
    a[:2, :2] = np.arange(4).reshape((2,2))
    return a[:2, :2]

这样不仅会返回修改后的子矩阵,还会改变原始数组本身。


对象是通过引用传递的,但它们的引用是通过值传递的:

就像Java一样,Python是通过值传递的,所以所有对象都是作为引用传递到函数中的,而这些引用是通过值传递的。

所以当你在函数内部索引这个数组对象并修改它的值时,你实际上是在修改这个引用指向的内存位置的值,但如果你改变引用本身,那么它不会修改原始对象,因为引用只是通过值传递的。

使用索引通过值传递对象的引用:

根据这个解释,你甚至可以进一步返回对象的引用,并在函数外部使用它来修改矩阵:

  1. 调用函数sub(a),它将返回子矩阵的引用,这个引用本身是原始矩阵的引用,通过值传递。
  2. 将这个函数调用的结果赋值给另一个变量,这样就将子矩阵的引用通过值传递给这个新变量,x = sub(a)
  3. 索引这个子矩阵以选择它的所有内容并修改它们的值:x[:] = np.ones((2,2))
  4. 这也会修改原始矩阵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]])
    

改变引用并不会改变对象:

  1. 然而,现在如果你将变量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]])
    

撰写回答