NumPy数组分配问题

38

我在Python 2.6.5中使用Numpy遇到了一个奇怪的问题。我先将一个numpy数组赋值给一个变量,然后再将一个新变量赋值为原变量。当我对新数组进行任何操作时,原始数组的值也会发生改变。为什么会这样?请看下面的示例。由于我对Python和编程一般都不太熟悉,所以请给我指点迷津。

-Sujan

>>> import numpy as np
>>> a = np.array([[1,2],[3,4]])
>>> b = a
>>> b
array([[1, 2],
       [3, 4]])
>>> c = a
>>> c
array([[1, 2],
       [3, 4]])
>>> c[:,1] = c[:,1] + 5
>>> c

array([[1, 7],
       [3, 9]])
>>> b
array([[1, 7],
       [3, 9]])
>>> a
array([[1, 7],
       [3, 9]])

2个回答

71

这实际上完全不是问题,这是Python中数组(和其他对象)的工作方式。

可以这样考虑:你在代码示例中创建的数组是一个对象,它位于内存中的某个位置。但是你不能通过告诉Python在内存中哪里查找它来在程序中使用它,你必须给它一个名称。当你写下

a = np.array([[1,2],[3,4]])

你既创建了数组,又创建了一个名字 a 来引用它。从那时起,Python 就知道 a 指向"内存地址0x123674283"(或其他地址)。在 Python 运行时有一个内部表格(如果我没记错的话称为 "符号表"),其中包含所有这些信息,因此在上述 Python 代码运行后,该表格将包含:

...,
'a' : 0x123674283,
...

当你将一个变量的值赋给另一个变量时,如下所示:

b = a

Python并没有复制整个数组,因为如果数组很大的话,会花费很长时间。相反,它会去符号表中将 a 的内存地址复制到 b 表的一个新行中。这样你最终得到的是:

...,
'a' : 0x123674283,
...,
'b' : 0x123674283,
...

因此,你可以看到,ab实际上是指向内存中相同的位置,也就是同一个对象。由于它们只是同一物体的两个名称,任何对其一个的更改都会反映在另一个上。

如果你想真正地复制数组,你必须显式调用一个方法来完成这个任务。Numpy数组有一个copy方法,你可以用它来实现这个目的。所以,如果你写:

b = a.copy()

那么Python首先会实际复制该数组 - 也就是说,它会设置一个新的内存区域,比如在地址0x123904381处,然后从内存地址0x123674283处复制数组的所有值到前面的内存中。因此,您拥有相同的内容同时存在于内存的两个不同位置。

...,
'a' : 0x123674283,
...,
'b' : 0x123904381,
...

现在,当您更改b中的一个元素时,该更改不会显示在a中,因为ab不再引用计算机内存的同一部分。由于数组数据有两个独立的副本,您可以更改一个而不影响另一个。


7
非常感谢你出色的描述。毫无疑问,这解决了问题,但我非常感激你花时间解释事情。你让我受益匪浅! - Sujan
1
Python不会复制整个数组,因为如果它是一个大数组,那么这将需要很长时间。我认为这与所需的时间少了一些关系,更多的是如果您不以这种方式实现,您需要引入像单独的指针类型之类的东西,而这些会使事情变得复杂。 - user2357112
使用C++语言,b = a 是一个指针赋值,而不是值的赋值。 - bscout11

4
简单来说,变量赋值会创建对现有对象的新引用。
  A = object   # A points to object in memory
  B = A        # B points to the same object

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接