numpy数组拷贝地址所引起的同步替换问题怎么解决
更新时间:2023-12-22问题描述
在使用numpy数组的过程中,我们有时候需要对一个已有的数组进行拷贝(或者截取),然后对这个拷贝数组进行一系列的操作,然后再将其赋值给原数组,以此达到更新原数组的目的。但是,这样做会引起一个问题:如果对拷贝的数组进行了改动,原数组同时也会被改动,这样就会引起同步替换的问题。下面我们来具体解释一下。
import numpy as np # 创建一个ndarray a = np.array([1, 2, 3, 4, 5]) # 将ndarray进行切片操作,相当于对原数组进行了拷贝操作 b = a[:] # 对拷贝数组进行改动 b[0] = 0 # 输出原数组的值 print(a) # 输出[0, 2, 3, 4, 5]
问题分析
以上的代码,我们首先创建了一个ndarray,然后对其进行切片操作,相当于生成了一个新的b数组,同时a数组和b数组是完全独立的,它们的物理存储空间不同。因此,当我们对b进行改动时(此处是将第一个元素赋值为0),a数组不会受到任何影响。但是,如果我们进行如下操作:
import numpy as np # 创建一个ndarray a = np.array([1, 2, 3, 4, 5]) # 将ndarray进行赋值操作,相当于对原数组进行了拷贝操作 b = a # 对拷贝数组进行改动 b[0] = 0 # 输出原数组的值 print(a) # 输出[0, 2, 3, 4, 5]
这里我们对a数组进行了赋值操作,由于赋值操作只是对a数组进行了另一个变量名的赋值,并没有进行拷贝操作,所以b数组和a数组是共享存储空间的,它们指向同一块物理内存。因此,当我们对b进行改动(此处是将第一个元素赋值为0)时,实际上是对a数组进行了改动,导致原数组的值也被改动,这就是同步替换的问题。
问题解决
既然我们已经明确了同步替换问题的原因,那么我们也可以采取一些具体的方式来解决这个问题。
1. 使用ndarray的copy()函数或者deepcopy()函数进行数组的拷贝
使用ndarray的copy()函数或者deepcopy()函数可以创建一个完全独立的新数组,此时我们对新数组的任何修改都不会对原数组造成影响。
import numpy as np # 创建一个ndarray a = np.array([1, 2, 3, 4, 5]) # 将ndarray进行拷贝操作 b = a.copy() # 对拷贝数组进行改动 b[0] = 0 # 输出原数组的值 print(a) # 输出[1, 2, 3, 4, 5]
2. 使用ndarray的view()函数进行数组的拷贝
使用ndarray的view()函数可以创建一个共享原数组数据的新数组,但是这个新数组的物理存储空间是独立的。这样我们就可以在原数组的基础上创建一个新数组,实现对原数组的拷贝,而且对新数组的任何修改都不会对原数组造成影响。
import numpy as np # 创建一个ndarray a = np.array([1, 2, 3, 4, 5]) # 将ndarray进行拷贝操作 b = a.view() # 对拷贝数组进行改动 b[0] = 0 # 输出原数组的值 print(a) # 输出[0, 2, 3, 4, 5]
总结
在日常的代码编写过程中,如果我们使用的是numpy数组,那么在进行拷贝操作时,一定要注意是否会出现同步替换的问题。如果直接对数组进行赋值操作,那么赋值的新数组和原数组会共享存储空间,此时我们对新数组的修改会同时作用于原数组。如果需要对数组进行修改操作,那么我们可以使用copy()函数或者deepcopy()函数创建一个完全独立的新数组,或者使用view()函数在原数组的基础上创建一个共享数据但是独立存储的新数组,这样就可以避免同步替换问题的出现。